Merge pull request #57591 from vnen/gdscript-enum-fixes
This commit is contained in:
commit
89eb6d372d
@ -356,6 +356,8 @@
|
|||||||
<member name="debug/gdscript/warnings/incompatible_ternary" type="bool" setter="" getter="" default="true">
|
<member name="debug/gdscript/warnings/incompatible_ternary" type="bool" setter="" getter="" default="true">
|
||||||
If [code]true[/code], enables warnings when a ternary operator may emit values with incompatible types.
|
If [code]true[/code], enables warnings when a ternary operator may emit values with incompatible types.
|
||||||
</member>
|
</member>
|
||||||
|
<member name="debug/gdscript/warnings/int_assigned_to_enum" type="bool" setter="" getter="" default="true">
|
||||||
|
</member>
|
||||||
<member name="debug/gdscript/warnings/integer_division" type="bool" setter="" getter="" default="true">
|
<member name="debug/gdscript/warnings/integer_division" type="bool" setter="" getter="" default="true">
|
||||||
If [code]true[/code], enables warnings when dividing an integer by another integer (the decimal part will be discarded).
|
If [code]true[/code], enables warnings when dividing an integer by another integer (the decimal part will be discarded).
|
||||||
</member>
|
</member>
|
||||||
|
@ -108,7 +108,7 @@ static GDScriptParser::DataType make_native_enum_type(const StringName &p_native
|
|||||||
GDScriptParser::DataType type;
|
GDScriptParser::DataType type;
|
||||||
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
|
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
|
||||||
type.kind = GDScriptParser::DataType::ENUM;
|
type.kind = GDScriptParser::DataType::ENUM;
|
||||||
type.builtin_type = Variant::OBJECT;
|
type.builtin_type = Variant::INT;
|
||||||
type.is_constant = true;
|
type.is_constant = true;
|
||||||
type.is_meta_type = true;
|
type.is_meta_type = true;
|
||||||
|
|
||||||
@ -650,9 +650,9 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
|
|||||||
datatype = specified_type;
|
datatype = specified_type;
|
||||||
|
|
||||||
if (member.variable->initializer != nullptr) {
|
if (member.variable->initializer != nullptr) {
|
||||||
if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true)) {
|
if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true, member.variable->initializer)) {
|
||||||
// Try reverse test since it can be a masked subtype.
|
// Try reverse test since it can be a masked subtype.
|
||||||
if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true)) {
|
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);
|
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 {
|
} else {
|
||||||
// TODO: Add warning.
|
// TODO: Add warning.
|
||||||
@ -1400,9 +1400,9 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
|
|||||||
type.is_meta_type = false;
|
type.is_meta_type = false;
|
||||||
|
|
||||||
if (p_variable->initializer != nullptr) {
|
if (p_variable->initializer != nullptr) {
|
||||||
if (!is_type_compatible(type, p_variable->initializer->get_datatype(), true)) {
|
if (!is_type_compatible(type, p_variable->initializer->get_datatype(), true, p_variable->initializer)) {
|
||||||
// Try reverse test since it can be a masked subtype.
|
// Try reverse test since it can be a masked subtype.
|
||||||
if (!is_type_compatible(p_variable->initializer->get_datatype(), type, true)) {
|
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);
|
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 {
|
} else {
|
||||||
// TODO: Add warning.
|
// TODO: Add warning.
|
||||||
@ -1877,11 +1877,11 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
|
|||||||
|
|
||||||
if (!assignee_type.is_variant() && assigned_value_type.is_hard_type()) {
|
if (!assignee_type.is_variant() && assigned_value_type.is_hard_type()) {
|
||||||
if (compatible) {
|
if (compatible) {
|
||||||
compatible = is_type_compatible(assignee_type, op_type, true);
|
compatible = is_type_compatible(assignee_type, op_type, true, p_assignment->assigned_value);
|
||||||
if (!compatible) {
|
if (!compatible) {
|
||||||
if (assignee_type.is_hard_type()) {
|
if (assignee_type.is_hard_type()) {
|
||||||
// Try reverse test since it can be a masked subtype.
|
// Try reverse test since it can be a masked subtype.
|
||||||
if (!is_type_compatible(op_type, assignee_type, true)) {
|
if (!is_type_compatible(op_type, assignee_type, true, p_assignment->assigned_value)) {
|
||||||
push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value);
|
push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value);
|
||||||
} else {
|
} else {
|
||||||
// TODO: Add warning.
|
// TODO: Add warning.
|
||||||
@ -2416,6 +2416,11 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
|
|||||||
}
|
}
|
||||||
validate_call_arg(par_types, default_arg_count, is_vararg, p_call);
|
validate_call_arg(par_types, default_arg_count, is_vararg, p_call);
|
||||||
|
|
||||||
|
if (base_type.kind == GDScriptParser::DataType::ENUM && base_type.is_meta_type) {
|
||||||
|
// Enum type is treated as a dictionary value for function calls.
|
||||||
|
base_type.is_meta_type = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (is_self && parser->current_function != nullptr && parser->current_function->is_static && !is_static) {
|
if (is_self && parser->current_function != nullptr && parser->current_function->is_static && !is_static) {
|
||||||
push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parser->current_function->identifier->name), p_call->callee);
|
push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parser->current_function->identifier->name), p_call->callee);
|
||||||
} else if (!is_self && base_type.is_meta_type && !is_static) {
|
} else if (!is_self && base_type.is_meta_type && !is_static) {
|
||||||
@ -2474,17 +2479,24 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
|
|||||||
GDScriptParser::DataType cast_type = resolve_datatype(p_cast->cast_type);
|
GDScriptParser::DataType cast_type = resolve_datatype(p_cast->cast_type);
|
||||||
|
|
||||||
if (!cast_type.is_set()) {
|
if (!cast_type.is_set()) {
|
||||||
|
mark_node_unsafe(p_cast);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cast_type.is_meta_type = false; // The casted value won't be a type name.
|
cast_type = type_from_metatype(cast_type); // The casted value won't be a type name.
|
||||||
p_cast->set_datatype(cast_type);
|
p_cast->set_datatype(cast_type);
|
||||||
|
|
||||||
if (!cast_type.is_variant()) {
|
if (!cast_type.is_variant()) {
|
||||||
GDScriptParser::DataType op_type = p_cast->operand->get_datatype();
|
GDScriptParser::DataType op_type = p_cast->operand->get_datatype();
|
||||||
if (!op_type.is_variant()) {
|
if (!op_type.is_variant()) {
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
if (op_type.kind == GDScriptParser::DataType::BUILTIN && cast_type.kind == GDScriptParser::DataType::BUILTIN) {
|
if (op_type.kind == GDScriptParser::DataType::ENUM && cast_type.kind == GDScriptParser::DataType::ENUM) {
|
||||||
|
// Enum types are compatible between each other, so it's a safe cast.
|
||||||
|
valid = true;
|
||||||
|
} else if (op_type.kind == GDScriptParser::DataType::BUILTIN && op_type.builtin_type == Variant::INT && cast_type.kind == GDScriptParser::DataType::ENUM) {
|
||||||
|
// Convertint int to enum is always valid.
|
||||||
|
valid = true;
|
||||||
|
} else if (op_type.kind == GDScriptParser::DataType::BUILTIN && cast_type.kind == GDScriptParser::DataType::BUILTIN) {
|
||||||
valid = Variant::can_convert(op_type.builtin_type, cast_type.builtin_type);
|
valid = Variant::can_convert(op_type.builtin_type, cast_type.builtin_type);
|
||||||
} else if (op_type.kind != GDScriptParser::DataType::BUILTIN && cast_type.kind != GDScriptParser::DataType::BUILTIN) {
|
} else if (op_type.kind != GDScriptParser::DataType::BUILTIN && cast_type.kind != GDScriptParser::DataType::BUILTIN) {
|
||||||
valid = is_type_compatible(cast_type, op_type) || is_type_compatible(op_type, cast_type);
|
valid = is_type_compatible(cast_type, op_type) || is_type_compatible(op_type, cast_type);
|
||||||
@ -2586,6 +2598,34 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
|
|||||||
|
|
||||||
const StringName &name = p_identifier->name;
|
const StringName &name = p_identifier->name;
|
||||||
|
|
||||||
|
if (base.kind == GDScriptParser::DataType::ENUM) {
|
||||||
|
if (base.is_meta_type) {
|
||||||
|
if (base.enum_values.has(name)) {
|
||||||
|
p_identifier->is_constant = true;
|
||||||
|
p_identifier->reduced_value = base.enum_values[name];
|
||||||
|
|
||||||
|
GDScriptParser::DataType result;
|
||||||
|
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
|
||||||
|
result.kind = GDScriptParser::DataType::ENUM;
|
||||||
|
result.is_constant = true;
|
||||||
|
result.builtin_type = Variant::INT;
|
||||||
|
result.native_type = base.native_type;
|
||||||
|
result.enum_type = base.enum_type;
|
||||||
|
p_identifier->set_datatype(result);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// Consider as a Dictionary, so it can be anything.
|
||||||
|
// This will be evaluated in the next if block.
|
||||||
|
base.kind = GDScriptParser::DataType::BUILTIN;
|
||||||
|
base.builtin_type = Variant::DICTIONARY;
|
||||||
|
base.is_meta_type = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
push_error(R"(Cannot get property from enum value.)", p_identifier);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (base.kind == GDScriptParser::DataType::BUILTIN) {
|
if (base.kind == GDScriptParser::DataType::BUILTIN) {
|
||||||
if (base.is_meta_type) {
|
if (base.is_meta_type) {
|
||||||
bool valid = true;
|
bool valid = true;
|
||||||
@ -2632,32 +2672,6 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (base.kind == GDScriptParser::DataType::ENUM) {
|
|
||||||
if (base.is_meta_type) {
|
|
||||||
if (base.enum_values.has(name)) {
|
|
||||||
p_identifier->is_constant = true;
|
|
||||||
p_identifier->reduced_value = base.enum_values[name];
|
|
||||||
|
|
||||||
GDScriptParser::DataType result;
|
|
||||||
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
|
|
||||||
result.kind = GDScriptParser::DataType::ENUM_VALUE;
|
|
||||||
result.is_constant = true;
|
|
||||||
result.builtin_type = Variant::INT;
|
|
||||||
result.native_type = base.native_type;
|
|
||||||
result.enum_type = name;
|
|
||||||
p_identifier->set_datatype(result);
|
|
||||||
} else {
|
|
||||||
// Consider as a Dictionary
|
|
||||||
GDScriptParser::DataType dummy;
|
|
||||||
dummy.kind = GDScriptParser::DataType::VARIANT;
|
|
||||||
p_identifier->set_datatype(dummy);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
push_error(R"(Cannot get property from enum value.)", p_identifier);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GDScriptParser::ClassNode *base_class = base.class_type;
|
GDScriptParser::ClassNode *base_class = base.class_type;
|
||||||
|
|
||||||
// TODO: Switch current class/function/suite here to avoid misrepresenting identifiers (in recursive reduce calls).
|
// TODO: Switch current class/function/suite here to avoid misrepresenting identifiers (in recursive reduce calls).
|
||||||
@ -2793,7 +2807,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
|
|||||||
if (element.identifier->name == p_identifier->name) {
|
if (element.identifier->name == p_identifier->name) {
|
||||||
GDScriptParser::DataType type;
|
GDScriptParser::DataType type;
|
||||||
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
|
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
|
||||||
type.kind = element.parent_enum->identifier ? GDScriptParser::DataType::ENUM_VALUE : GDScriptParser::DataType::BUILTIN;
|
type.kind = element.parent_enum->identifier ? GDScriptParser::DataType::ENUM : GDScriptParser::DataType::BUILTIN;
|
||||||
type.builtin_type = Variant::INT;
|
type.builtin_type = Variant::INT;
|
||||||
type.is_constant = true;
|
type.is_constant = true;
|
||||||
if (element.parent_enum->identifier) {
|
if (element.parent_enum->identifier) {
|
||||||
@ -3493,6 +3507,9 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_metatype(const GDScriptPars
|
|||||||
GDScriptParser::DataType result = p_meta_type;
|
GDScriptParser::DataType result = p_meta_type;
|
||||||
result.is_meta_type = false;
|
result.is_meta_type = false;
|
||||||
result.is_constant = false;
|
result.is_constant = false;
|
||||||
|
if (p_meta_type.kind == GDScriptParser::DataType::ENUM) {
|
||||||
|
result.builtin_type = Variant::INT;
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3549,6 +3566,18 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source
|
|||||||
r_default_arg_count = 0;
|
r_default_arg_count = 0;
|
||||||
StringName function_name = p_function;
|
StringName function_name = p_function;
|
||||||
|
|
||||||
|
if (p_base_type.kind == GDScriptParser::DataType::ENUM) {
|
||||||
|
if (p_base_type.is_meta_type) {
|
||||||
|
// Enum type can be treated as a dictionary value.
|
||||||
|
p_base_type.kind = GDScriptParser::DataType::BUILTIN;
|
||||||
|
p_base_type.builtin_type = Variant::DICTIONARY;
|
||||||
|
p_base_type.is_meta_type = false;
|
||||||
|
} else {
|
||||||
|
push_error("Cannot call function on enum value.", p_source);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (p_base_type.kind == GDScriptParser::DataType::BUILTIN) {
|
if (p_base_type.kind == GDScriptParser::DataType::BUILTIN) {
|
||||||
// Construct a base type to get methods.
|
// Construct a base type to get methods.
|
||||||
Callable::CallError err;
|
Callable::CallError err;
|
||||||
@ -3799,6 +3828,22 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
|
|||||||
|
|
||||||
Variant::Type a_type = p_a.builtin_type;
|
Variant::Type a_type = p_a.builtin_type;
|
||||||
Variant::Type b_type = p_b.builtin_type;
|
Variant::Type b_type = p_b.builtin_type;
|
||||||
|
|
||||||
|
if (p_a.kind == GDScriptParser::DataType::ENUM) {
|
||||||
|
if (p_a.is_meta_type) {
|
||||||
|
a_type = Variant::DICTIONARY;
|
||||||
|
} else {
|
||||||
|
a_type = Variant::INT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (p_b.kind == GDScriptParser::DataType::ENUM) {
|
||||||
|
if (p_b.is_meta_type) {
|
||||||
|
b_type = Variant::DICTIONARY;
|
||||||
|
} else {
|
||||||
|
b_type = Variant::INT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Variant::ValidatedOperatorEvaluator op_eval = Variant::get_validated_operator_evaluator(p_operation, a_type, b_type);
|
Variant::ValidatedOperatorEvaluator op_eval = Variant::get_validated_operator_evaluator(p_operation, a_type, b_type);
|
||||||
|
|
||||||
bool hard_operation = p_a.is_hard_type() && p_b.is_hard_type();
|
bool hard_operation = p_a.is_hard_type() && p_b.is_hard_type();
|
||||||
@ -3828,7 +3873,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add safe/unsafe return variable (for variant cases)
|
// TODO: Add safe/unsafe return variable (for variant cases)
|
||||||
bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion) const {
|
bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion, const GDScriptParser::Node *p_source_node) {
|
||||||
// These return "true" so it doesn't affect users negatively.
|
// These return "true" so it doesn't affect users negatively.
|
||||||
ERR_FAIL_COND_V_MSG(!p_target.is_set(), true, "Parser bug (please report): Trying to check compatibility of unset target type");
|
ERR_FAIL_COND_V_MSG(!p_target.is_set(), true, "Parser bug (please report): Trying to check compatibility of unset target type");
|
||||||
ERR_FAIL_COND_V_MSG(!p_source.is_set(), true, "Parser bug (please report): Trying to check compatibility of unset value type");
|
ERR_FAIL_COND_V_MSG(!p_source.is_set(), true, "Parser bug (please report): Trying to check compatibility of unset value type");
|
||||||
@ -3848,7 +3893,7 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
|
|||||||
if (!valid && p_allow_implicit_conversion) {
|
if (!valid && p_allow_implicit_conversion) {
|
||||||
valid = Variant::can_convert_strict(p_source.builtin_type, p_target.builtin_type);
|
valid = Variant::can_convert_strict(p_source.builtin_type, p_target.builtin_type);
|
||||||
}
|
}
|
||||||
if (!valid && p_target.builtin_type == Variant::INT && p_source.kind == GDScriptParser::DataType::ENUM_VALUE) {
|
if (!valid && p_target.builtin_type == Variant::INT && p_source.kind == GDScriptParser::DataType::ENUM && !p_source.is_meta_type) {
|
||||||
// Enum value is also integer.
|
// Enum value is also integer.
|
||||||
valid = true;
|
valid = true;
|
||||||
}
|
}
|
||||||
@ -3869,6 +3914,11 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
|
|||||||
|
|
||||||
if (p_target.kind == GDScriptParser::DataType::ENUM) {
|
if (p_target.kind == GDScriptParser::DataType::ENUM) {
|
||||||
if (p_source.kind == GDScriptParser::DataType::BUILTIN && p_source.builtin_type == Variant::INT) {
|
if (p_source.kind == GDScriptParser::DataType::BUILTIN && p_source.builtin_type == Variant::INT) {
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
if (p_source_node) {
|
||||||
|
parser->push_warning(p_source_node, GDScriptWarning::INT_ASSIGNED_TO_ENUM);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (p_source.kind == GDScriptParser::DataType::ENUM) {
|
if (p_source.kind == GDScriptParser::DataType::ENUM) {
|
||||||
@ -3876,11 +3926,6 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (p_source.kind == GDScriptParser::DataType::ENUM_VALUE) {
|
|
||||||
if (p_source.native_type == p_target.native_type && p_target.enum_values.has(p_source.enum_type)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3935,7 +3980,6 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
|
|||||||
case GDScriptParser::DataType::VARIANT:
|
case GDScriptParser::DataType::VARIANT:
|
||||||
case GDScriptParser::DataType::BUILTIN:
|
case GDScriptParser::DataType::BUILTIN:
|
||||||
case GDScriptParser::DataType::ENUM:
|
case GDScriptParser::DataType::ENUM:
|
||||||
case GDScriptParser::DataType::ENUM_VALUE:
|
|
||||||
case GDScriptParser::DataType::UNRESOLVED:
|
case GDScriptParser::DataType::UNRESOLVED:
|
||||||
break; // Already solved before.
|
break; // Already solved before.
|
||||||
}
|
}
|
||||||
@ -3972,7 +4016,6 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
|
|||||||
case GDScriptParser::DataType::VARIANT:
|
case GDScriptParser::DataType::VARIANT:
|
||||||
case GDScriptParser::DataType::BUILTIN:
|
case GDScriptParser::DataType::BUILTIN:
|
||||||
case GDScriptParser::DataType::ENUM:
|
case GDScriptParser::DataType::ENUM:
|
||||||
case GDScriptParser::DataType::ENUM_VALUE:
|
|
||||||
case GDScriptParser::DataType::UNRESOLVED:
|
case GDScriptParser::DataType::UNRESOLVED:
|
||||||
break; // Already solved before.
|
break; // Already solved before.
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ class GDScriptAnalyzer {
|
|||||||
GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source);
|
GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source);
|
||||||
GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source);
|
GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source);
|
||||||
void update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal);
|
void update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal);
|
||||||
bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false) const;
|
bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr);
|
||||||
void push_error(const String &p_message, const GDScriptParser::Node *p_origin);
|
void push_error(const String &p_message, const GDScriptParser::Node *p_origin);
|
||||||
void mark_node_unsafe(const GDScriptParser::Node *p_node);
|
void mark_node_unsafe(const GDScriptParser::Node *p_node);
|
||||||
bool class_exists(const StringName &p_class) const;
|
bool class_exists(const StringName &p_class) const;
|
||||||
|
@ -141,10 +141,13 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
|
|||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case GDScriptParser::DataType::ENUM:
|
case GDScriptParser::DataType::ENUM:
|
||||||
case GDScriptParser::DataType::ENUM_VALUE:
|
|
||||||
result.has_type = true;
|
result.has_type = true;
|
||||||
result.kind = GDScriptDataType::BUILTIN;
|
result.kind = GDScriptDataType::BUILTIN;
|
||||||
result.builtin_type = Variant::INT;
|
if (p_datatype.is_meta_type) {
|
||||||
|
result.builtin_type = Variant::DICTIONARY;
|
||||||
|
} else {
|
||||||
|
result.builtin_type = Variant::INT;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case GDScriptParser::DataType::UNRESOLVED: {
|
case GDScriptParser::DataType::UNRESOLVED: {
|
||||||
ERR_PRINT("Parser bug: converting unresolved type.");
|
ERR_PRINT("Parser bug: converting unresolved type.");
|
||||||
@ -469,7 +472,14 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
|
|||||||
} break;
|
} break;
|
||||||
case GDScriptParser::Node::CAST: {
|
case GDScriptParser::Node::CAST: {
|
||||||
const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);
|
const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);
|
||||||
GDScriptDataType cast_type = _gdtype_from_datatype(cn->cast_type->get_datatype());
|
GDScriptParser::DataType og_cast_type = cn->cast_type->get_datatype();
|
||||||
|
GDScriptDataType cast_type = _gdtype_from_datatype(og_cast_type);
|
||||||
|
|
||||||
|
if (og_cast_type.kind == GDScriptParser::DataType::ENUM) {
|
||||||
|
// Enum types are usually treated as dictionaries, but in this case we want to cast to an integer.
|
||||||
|
cast_type.kind = GDScriptDataType::BUILTIN;
|
||||||
|
cast_type.builtin_type = Variant::INT;
|
||||||
|
}
|
||||||
|
|
||||||
// Create temporary for result first since it will be deleted last.
|
// Create temporary for result first since it will be deleted last.
|
||||||
GDScriptCodeGenerator::Address result = codegen.add_temporary(cast_type);
|
GDScriptCodeGenerator::Address result = codegen.add_temporary(cast_type);
|
||||||
|
@ -610,7 +610,7 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio
|
|||||||
case GDScriptParser::Node::SUBSCRIPT: {
|
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->default_value);
|
||||||
if (sub->is_constant) {
|
if (sub->is_constant) {
|
||||||
if (sub->datatype.kind == GDScriptParser::DataType::ENUM_VALUE) {
|
if (sub->datatype.kind == GDScriptParser::DataType::ENUM) {
|
||||||
def_val = sub->get_datatype().to_string();
|
def_val = sub->get_datatype().to_string();
|
||||||
} else if (sub->reduced) {
|
} else if (sub->reduced) {
|
||||||
const Variant::Type vt = sub->reduced_value.get_type();
|
const Variant::Type vt = sub->reduced_value.get_type();
|
||||||
|
@ -3740,8 +3740,6 @@ String GDScriptParser::DataType::to_string() const {
|
|||||||
}
|
}
|
||||||
case ENUM:
|
case ENUM:
|
||||||
return enum_type.operator String() + " (enum)";
|
return enum_type.operator String() + " (enum)";
|
||||||
case ENUM_VALUE:
|
|
||||||
return enum_type.operator String() + " (enum value)";
|
|
||||||
case UNRESOLVED:
|
case UNRESOLVED:
|
||||||
return "<unresolved type>";
|
return "<unresolved type>";
|
||||||
}
|
}
|
||||||
|
@ -106,8 +106,7 @@ public:
|
|||||||
NATIVE,
|
NATIVE,
|
||||||
SCRIPT,
|
SCRIPT,
|
||||||
CLASS, // GDScript.
|
CLASS, // GDScript.
|
||||||
ENUM, // Full enumeration.
|
ENUM, // Enumeration.
|
||||||
ENUM_VALUE, // Value from enumeration.
|
|
||||||
VARIANT, // Can be any type.
|
VARIANT, // Can be any type.
|
||||||
UNRESOLVED,
|
UNRESOLVED,
|
||||||
};
|
};
|
||||||
@ -185,8 +184,6 @@ public:
|
|||||||
return builtin_type == p_other.builtin_type;
|
return builtin_type == p_other.builtin_type;
|
||||||
case NATIVE:
|
case NATIVE:
|
||||||
case ENUM:
|
case ENUM:
|
||||||
return native_type == p_other.native_type;
|
|
||||||
case ENUM_VALUE:
|
|
||||||
return native_type == p_other.native_type && enum_type == p_other.enum_type;
|
return native_type == p_other.native_type && enum_type == p_other.enum_type;
|
||||||
case SCRIPT:
|
case SCRIPT:
|
||||||
return script_type == p_other.script_type;
|
return script_type == p_other.script_type;
|
||||||
|
@ -152,6 +152,9 @@ String GDScriptWarning::get_message() const {
|
|||||||
CHECK_SYMBOLS(3);
|
CHECK_SYMBOLS(3);
|
||||||
return vformat(R"(The %s '%s' has the same name as a %s.)", symbols[0], symbols[1], symbols[2]);
|
return vformat(R"(The %s '%s' has the same name as a %s.)", symbols[0], symbols[1], symbols[2]);
|
||||||
}
|
}
|
||||||
|
case INT_ASSIGNED_TO_ENUM: {
|
||||||
|
return "Integer used when an enum value is expected. If this is intended cast the integer to the enum type.";
|
||||||
|
}
|
||||||
case WARNING_MAX:
|
case WARNING_MAX:
|
||||||
break; // Can't happen, but silences warning
|
break; // Can't happen, but silences warning
|
||||||
}
|
}
|
||||||
@ -199,6 +202,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
|
|||||||
"REDUNDANT_AWAIT",
|
"REDUNDANT_AWAIT",
|
||||||
"EMPTY_FILE",
|
"EMPTY_FILE",
|
||||||
"SHADOWED_GLOBAL_IDENTIFIER",
|
"SHADOWED_GLOBAL_IDENTIFIER",
|
||||||
|
"INT_ASSIGNED_TO_ENUM",
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");
|
static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");
|
||||||
|
@ -70,6 +70,7 @@ public:
|
|||||||
REDUNDANT_AWAIT, // await is used but expression is synchronous (not a signal nor a coroutine).
|
REDUNDANT_AWAIT, // await is used but expression is synchronous (not a signal nor a coroutine).
|
||||||
EMPTY_FILE, // A script file is empty.
|
EMPTY_FILE, // A script file is empty.
|
||||||
SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable.
|
SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable.
|
||||||
|
INT_ASSIGNED_TO_ENUM, // An integer value was assigned to an enum-typed variable without casting.
|
||||||
WARNING_MAX,
|
WARNING_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -134,12 +134,14 @@ GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_l
|
|||||||
if (do_init_languages) {
|
if (do_init_languages) {
|
||||||
init_language(p_source_dir);
|
init_language(p_source_dir);
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
// Enable all warnings for GDScript, so we can test them.
|
// Enable all warnings for GDScript, so we can test them.
|
||||||
ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/enable", true);
|
ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/enable", true);
|
||||||
for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
|
for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
|
||||||
String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower();
|
String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower();
|
||||||
ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/" + warning, true);
|
ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/" + warning, true);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Enable printing to show results
|
// Enable printing to show results
|
||||||
_print_line_enabled = true;
|
_print_line_enabled = true;
|
||||||
@ -153,6 +155,21 @@ GDScriptTestRunner::~GDScriptTestRunner() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef DEBUG_ENABLED
|
||||||
|
static String strip_warnings(const String &p_expected) {
|
||||||
|
// On release builds we don't have warnings. Here we remove them from the output before comparison
|
||||||
|
// so it doesn't fail just because of difference in warnings.
|
||||||
|
String expected_no_warnings;
|
||||||
|
for (String line : p_expected.split("\n")) {
|
||||||
|
if (line.begins_with(">> ")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
expected_no_warnings += line + "\n";
|
||||||
|
}
|
||||||
|
return expected_no_warnings.strip_edges() + "\n";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int GDScriptTestRunner::run_tests() {
|
int GDScriptTestRunner::run_tests() {
|
||||||
if (!make_tests()) {
|
if (!make_tests()) {
|
||||||
FAIL("An error occurred while making the tests.");
|
FAIL("An error occurred while making the tests.");
|
||||||
@ -170,6 +187,9 @@ int GDScriptTestRunner::run_tests() {
|
|||||||
GDScriptTest::TestResult result = test.run_test();
|
GDScriptTest::TestResult result = test.run_test();
|
||||||
|
|
||||||
String expected = FileAccess::get_file_as_string(test.get_output_file());
|
String expected = FileAccess::get_file_as_string(test.get_output_file());
|
||||||
|
#ifndef DEBUG_ENABLED
|
||||||
|
expected = strip_warnings(expected);
|
||||||
|
#endif
|
||||||
INFO(test.get_source_file());
|
INFO(test.get_source_file());
|
||||||
if (!result.passed) {
|
if (!result.passed) {
|
||||||
INFO(expected);
|
INFO(expected);
|
||||||
@ -233,6 +253,22 @@ bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (next.get_extension().to_lower() == "gd") {
|
if (next.get_extension().to_lower() == "gd") {
|
||||||
|
#ifndef DEBUG_ENABLED
|
||||||
|
// On release builds, skip tests marked as debug only.
|
||||||
|
Error open_err = OK;
|
||||||
|
FileAccessRef script_file(FileAccess::open(current_dir.plus_file(next), FileAccess::READ, &open_err));
|
||||||
|
if (open_err != OK) {
|
||||||
|
ERR_PRINT(vformat(R"(Couldn't open test file "%s".)", next));
|
||||||
|
next = dir->get_next();
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
if (script_file->get_line() == "#debug-only") {
|
||||||
|
next = dir->get_next();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
String out_file = next.get_basename() + ".out";
|
String out_file = next.get_basename() + ".out";
|
||||||
if (!is_generating && !dir->file_exists(out_file)) {
|
if (!is_generating && !dir->file_exists(out_file)) {
|
||||||
ERR_FAIL_V_MSG(false, "Could not find output file for " + next);
|
ERR_FAIL_V_MSG(false, "Could not find output file for " + next);
|
||||||
@ -387,6 +423,10 @@ bool GDScriptTest::check_output(const String &p_output) const {
|
|||||||
String got = p_output.strip_edges(); // TODO: may be hacky.
|
String got = p_output.strip_edges(); // TODO: may be hacky.
|
||||||
got += "\n"; // Make sure to insert newline for CI static checks.
|
got += "\n"; // Make sure to insert newline for CI static checks.
|
||||||
|
|
||||||
|
#ifndef DEBUG_ENABLED
|
||||||
|
expected = strip_warnings(expected);
|
||||||
|
#endif
|
||||||
|
|
||||||
return got == expected;
|
return got == expected;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -469,6 +509,7 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
StringBuilder warning_string;
|
StringBuilder warning_string;
|
||||||
for (const GDScriptWarning &E : parser.get_warnings()) {
|
for (const GDScriptWarning &E : parser.get_warnings()) {
|
||||||
const GDScriptWarning warning = E;
|
const GDScriptWarning warning = E;
|
||||||
@ -482,6 +523,7 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
|
|||||||
warning_string.append("\n");
|
warning_string.append("\n");
|
||||||
}
|
}
|
||||||
result.output += warning_string.as_string();
|
result.output += warning_string.as_string();
|
||||||
|
#endif
|
||||||
|
|
||||||
// Test compiling.
|
// Test compiling.
|
||||||
GDScriptCompiler compiler;
|
GDScriptCompiler compiler;
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
|
||||||
|
enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
|
||||||
|
|
||||||
|
# Different enum types can't be assigned without casting.
|
||||||
|
var class_var: MyEnum = MyEnum.ENUM_VALUE_1
|
||||||
|
|
||||||
|
func test():
|
||||||
|
print(class_var)
|
||||||
|
class_var = MyOtherEnum.OTHER_ENUM_VALUE_2
|
||||||
|
print(class_var)
|
@ -0,0 +1,2 @@
|
|||||||
|
GDTEST_ANALYZER_ERROR
|
||||||
|
Cannot assign a value of type "MyOtherEnum (enum)" to a target of type "MyEnum (enum)".
|
@ -0,0 +1,8 @@
|
|||||||
|
enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
|
||||||
|
enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
|
||||||
|
|
||||||
|
# Different enum types can't be assigned without casting.
|
||||||
|
var class_var: MyEnum = MyOtherEnum.OTHER_ENUM_VALUE_1
|
||||||
|
|
||||||
|
func test():
|
||||||
|
print(class_var)
|
@ -0,0 +1,2 @@
|
|||||||
|
GDTEST_ANALYZER_ERROR
|
||||||
|
Value of type "MyOtherEnum (enum)" cannot be assigned to a variable of type "MyEnum (enum)".
|
@ -0,0 +1,8 @@
|
|||||||
|
enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
|
||||||
|
enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
|
||||||
|
|
||||||
|
func test():
|
||||||
|
var local_var: MyEnum = MyEnum.ENUM_VALUE_1
|
||||||
|
print(local_var)
|
||||||
|
local_var = MyOtherEnum.OTHER_ENUM_VALUE_2
|
||||||
|
print(local_var)
|
@ -0,0 +1,2 @@
|
|||||||
|
GDTEST_ANALYZER_ERROR
|
||||||
|
Cannot assign a value of type "MyOtherEnum (enum)" to a target of type "MyEnum (enum)".
|
@ -0,0 +1,6 @@
|
|||||||
|
enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
|
||||||
|
enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
|
||||||
|
|
||||||
|
func test():
|
||||||
|
var local_var: MyEnum = MyOtherEnum.OTHER_ENUM_VALUE_1
|
||||||
|
print(local_var)
|
@ -0,0 +1,2 @@
|
|||||||
|
GDTEST_ANALYZER_ERROR
|
||||||
|
Value of type "MyOtherEnum (enum)" cannot be assigned to a variable of type "MyEnum (enum)".
|
@ -0,0 +1,13 @@
|
|||||||
|
enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
|
||||||
|
|
||||||
|
var class_var: int = MyEnum.ENUM_VALUE_1
|
||||||
|
|
||||||
|
func test():
|
||||||
|
print(class_var)
|
||||||
|
class_var = MyEnum.ENUM_VALUE_2
|
||||||
|
print(class_var)
|
||||||
|
|
||||||
|
var local_var: int = MyEnum.ENUM_VALUE_1
|
||||||
|
print(local_var)
|
||||||
|
local_var = MyEnum.ENUM_VALUE_2
|
||||||
|
print(local_var)
|
@ -0,0 +1,5 @@
|
|||||||
|
GDTEST_OK
|
||||||
|
0
|
||||||
|
1
|
||||||
|
0
|
||||||
|
1
|
@ -0,0 +1,13 @@
|
|||||||
|
enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
|
||||||
|
|
||||||
|
var class_var: MyEnum = 0 as MyEnum
|
||||||
|
|
||||||
|
func test():
|
||||||
|
print(class_var)
|
||||||
|
class_var = 1 as MyEnum
|
||||||
|
print(class_var)
|
||||||
|
|
||||||
|
var local_var: MyEnum = 0 as MyEnum
|
||||||
|
print(local_var)
|
||||||
|
local_var = 1 as MyEnum
|
||||||
|
print(local_var)
|
@ -0,0 +1,5 @@
|
|||||||
|
GDTEST_OK
|
||||||
|
0
|
||||||
|
1
|
||||||
|
0
|
||||||
|
1
|
@ -0,0 +1,14 @@
|
|||||||
|
enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
|
||||||
|
enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
|
||||||
|
|
||||||
|
var class_var: MyEnum = MyOtherEnum.OTHER_ENUM_VALUE_1 as MyEnum
|
||||||
|
|
||||||
|
func test():
|
||||||
|
print(class_var)
|
||||||
|
class_var = MyOtherEnum.OTHER_ENUM_VALUE_2 as MyEnum
|
||||||
|
print(class_var)
|
||||||
|
|
||||||
|
var local_var: MyEnum = MyOtherEnum.OTHER_ENUM_VALUE_1 as MyEnum
|
||||||
|
print(local_var)
|
||||||
|
local_var = MyOtherEnum.OTHER_ENUM_VALUE_2 as MyEnum
|
||||||
|
print(local_var)
|
@ -0,0 +1,5 @@
|
|||||||
|
GDTEST_OK
|
||||||
|
0
|
||||||
|
1
|
||||||
|
0
|
||||||
|
1
|
@ -0,0 +1,13 @@
|
|||||||
|
enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
|
||||||
|
|
||||||
|
var class_var: MyEnum = MyEnum.ENUM_VALUE_1
|
||||||
|
|
||||||
|
func test():
|
||||||
|
print(class_var)
|
||||||
|
class_var = MyEnum.ENUM_VALUE_2
|
||||||
|
print(class_var)
|
||||||
|
|
||||||
|
var local_var: MyEnum = MyEnum.ENUM_VALUE_1
|
||||||
|
print(local_var)
|
||||||
|
local_var = MyEnum.ENUM_VALUE_2
|
||||||
|
print(local_var)
|
@ -0,0 +1,5 @@
|
|||||||
|
GDTEST_OK
|
||||||
|
0
|
||||||
|
1
|
||||||
|
0
|
||||||
|
1
|
@ -0,0 +1,21 @@
|
|||||||
|
# Enum is equivalent to int for comparisons and operations.
|
||||||
|
enum MyEnum {
|
||||||
|
ZERO,
|
||||||
|
ONE,
|
||||||
|
TWO,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OtherEnum {
|
||||||
|
ZERO,
|
||||||
|
ONE,
|
||||||
|
TWO,
|
||||||
|
}
|
||||||
|
|
||||||
|
func test():
|
||||||
|
print(MyEnum.ZERO == OtherEnum.ZERO)
|
||||||
|
print(MyEnum.ZERO == 1)
|
||||||
|
print(MyEnum.ZERO != OtherEnum.ONE)
|
||||||
|
print(MyEnum.ZERO != 0)
|
||||||
|
|
||||||
|
print(MyEnum.ONE + OtherEnum.TWO)
|
||||||
|
print(2 - MyEnum.ONE)
|
@ -0,0 +1,7 @@
|
|||||||
|
GDTEST_OK
|
||||||
|
true
|
||||||
|
false
|
||||||
|
true
|
||||||
|
false
|
||||||
|
3
|
||||||
|
1
|
@ -0,0 +1,13 @@
|
|||||||
|
enum MyEnum {
|
||||||
|
ZERO,
|
||||||
|
ONE,
|
||||||
|
TWO,
|
||||||
|
}
|
||||||
|
|
||||||
|
func test():
|
||||||
|
for key in MyEnum.keys():
|
||||||
|
prints(key, MyEnum[key])
|
||||||
|
|
||||||
|
# https://github.com/godotengine/godot/issues/55491
|
||||||
|
for key in MyEnum:
|
||||||
|
prints(key, MyEnum[key])
|
@ -0,0 +1,7 @@
|
|||||||
|
GDTEST_OK
|
||||||
|
ZERO 0
|
||||||
|
ONE 1
|
||||||
|
TWO 2
|
||||||
|
ZERO 0
|
||||||
|
ONE 1
|
||||||
|
TWO 2
|
@ -0,0 +1,15 @@
|
|||||||
|
enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
|
||||||
|
|
||||||
|
# Assigning int value to enum-typed variable without explicit cast causes a warning.
|
||||||
|
# While it is valid it may be a mistake in the assignment.
|
||||||
|
var class_var: MyEnum = 0
|
||||||
|
|
||||||
|
func test():
|
||||||
|
print(class_var)
|
||||||
|
class_var = 1
|
||||||
|
print(class_var)
|
||||||
|
|
||||||
|
var local_var: MyEnum = 0
|
||||||
|
print(local_var)
|
||||||
|
local_var = 1
|
||||||
|
print(local_var)
|
@ -0,0 +1,21 @@
|
|||||||
|
GDTEST_OK
|
||||||
|
>> WARNING
|
||||||
|
>> Line: 5
|
||||||
|
>> INT_ASSIGNED_TO_ENUM
|
||||||
|
>> Integer used when an enum value is expected. If this is intended cast the integer to the enum type.
|
||||||
|
>> WARNING
|
||||||
|
>> Line: 9
|
||||||
|
>> INT_ASSIGNED_TO_ENUM
|
||||||
|
>> Integer used when an enum value is expected. If this is intended cast the integer to the enum type.
|
||||||
|
>> WARNING
|
||||||
|
>> Line: 12
|
||||||
|
>> INT_ASSIGNED_TO_ENUM
|
||||||
|
>> Integer used when an enum value is expected. If this is intended cast the integer to the enum type.
|
||||||
|
>> WARNING
|
||||||
|
>> Line: 14
|
||||||
|
>> INT_ASSIGNED_TO_ENUM
|
||||||
|
>> Integer used when an enum value is expected. If this is intended cast the integer to the enum type.
|
||||||
|
0
|
||||||
|
1
|
||||||
|
0
|
||||||
|
1
|
@ -1,3 +1,4 @@
|
|||||||
|
#debug-only
|
||||||
func test():
|
func test():
|
||||||
var node := Node.new()
|
var node := Node.new()
|
||||||
var inside_tree = node.is_inside_tree
|
var inside_tree = node.is_inside_tree
|
||||||
|
@ -2,5 +2,5 @@ GDTEST_RUNTIME_ERROR
|
|||||||
>> SCRIPT ERROR
|
>> SCRIPT ERROR
|
||||||
>> on function: test()
|
>> on function: test()
|
||||||
>> runtime/errors/callable_call_after_free_object.gd
|
>> runtime/errors/callable_call_after_free_object.gd
|
||||||
>> 5
|
>> 6
|
||||||
>> Attempt to call function 'null::is_inside_tree (Callable)' on a null instance.
|
>> Attempt to call function 'null::is_inside_tree (Callable)' on a null instance.
|
||||||
|
Loading…
Reference in New Issue
Block a user