1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-05 12:10:55 +00:00

GDScript: Treat enum values as int and enum types as dictionary

Since enums resolve to a dictionary at runtime, calling dictionary
methods on an enum type is a valid use case. This ensures this is true
by adding test cases. This also makes enum values be treated as ints
when used in operations.
This commit is contained in:
George Marques
2022-02-02 13:57:24 -03:00
parent b013c0d544
commit ceafdf347e
6 changed files with 127 additions and 31 deletions

View File

@@ -108,7 +108,7 @@ static GDScriptParser::DataType make_native_enum_type(const StringName &p_native
GDScriptParser::DataType type;
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
type.kind = GDScriptParser::DataType::ENUM;
type.builtin_type = Variant::OBJECT;
type.builtin_type = Variant::INT;
type.is_constant = true;
type.is_meta_type = true;
@@ -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);
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) {
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) {
@@ -2478,7 +2483,7 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
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);
if (!cast_type.is_variant()) {
@@ -2593,6 +2598,34 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
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.is_meta_type) {
bool valid = true;
@@ -2639,33 +2672,6 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
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;
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);
} else {
// Consider as a Dictionary, so it can be anything.
GDScriptParser::DataType dummy;
dummy.type_source = GDScriptParser::DataType::UNDETECTED;
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;
// TODO: Switch current class/function/suite here to avoid misrepresenting identifiers (in recursive reduce calls).
@@ -3501,6 +3507,9 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_metatype(const GDScriptPars
GDScriptParser::DataType result = p_meta_type;
result.is_meta_type = false;
result.is_constant = false;
if (p_meta_type.kind == GDScriptParser::DataType::ENUM) {
result.builtin_type = Variant::INT;
}
return result;
}
@@ -3557,6 +3566,18 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source
r_default_arg_count = 0;
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) {
// Construct a base type to get methods.
Callable::CallError err;
@@ -3807,6 +3828,22 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
Variant::Type a_type = p_a.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);
bool hard_operation = p_a.is_hard_type() && p_b.is_hard_type();