Merge pull request #73590 from vnen/gdscript-global-scope-enums

Make global scope enums accessible as types in GDScript
This commit is contained in:
Rémi Verschelde 2023-02-20 15:41:45 +01:00
commit 2057d7344e
No known key found for this signature in database
GPG Key ID: C3336907360768E1
21 changed files with 355 additions and 87 deletions

View File

@ -33,14 +33,15 @@
#include "core/input/input_event.h"
#include "core/object/class_db.h"
#include "core/os/keyboard.h"
#include "core/templates/hash_set.h"
#include "core/variant/variant.h"
struct _CoreConstant {
#ifdef DEBUG_METHODS_ENABLED
StringName enum_name;
bool ignore_value_in_docs = false;
bool is_bitfield = false;
#endif
StringName enum_name;
const char *name = nullptr;
int64_t value = 0;
@ -48,14 +49,15 @@ struct _CoreConstant {
#ifdef DEBUG_METHODS_ENABLED
_CoreConstant(const StringName &p_enum_name, const char *p_name, int64_t p_value, bool p_ignore_value_in_docs = false, bool p_is_bitfield = false) :
enum_name(p_enum_name),
ignore_value_in_docs(p_ignore_value_in_docs),
is_bitfield(p_is_bitfield),
enum_name(p_enum_name),
name(p_name),
value(p_value) {
}
#else
_CoreConstant(const char *p_name, int64_t p_value) :
_CoreConstant(const StringName &p_enum_name, const char *p_name, int64_t p_value) :
enum_name(p_enum_name),
name(p_name),
value(p_value) {
}
@ -63,84 +65,190 @@ struct _CoreConstant {
};
static Vector<_CoreConstant> _global_constants;
static HashMap<StringName, int> _global_constants_map;
static HashMap<StringName, Vector<_CoreConstant>> _global_enums;
#ifdef DEBUG_METHODS_ENABLED
#define BIND_CORE_CONSTANT(m_constant) \
_global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant));
#define BIND_CORE_CONSTANT(m_constant) \
_global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1;
#define BIND_CORE_ENUM_CONSTANT(m_constant) \
_global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant));
#define BIND_CORE_ENUM_CONSTANT(m_constant) \
{ \
StringName enum_name = __constant_get_enum_name(m_constant, #m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_BITFIELD_FLAG(m_constant) \
_global_constants.push_back(_CoreConstant(__constant_get_bitfield_name(m_constant, #m_constant), #m_constant, m_constant, false, true));
#define BIND_CORE_BITFIELD_FLAG(m_constant) \
{ \
StringName enum_name = __constant_get_bitfield_name(m_constant, #m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant, false, true)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
// This just binds enum classes as if they were regular enum constants.
#define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member) \
_global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_enum::m_member, #m_prefix "_" #m_member), #m_prefix "_" #m_member, (int64_t)m_enum::m_member));
#define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member) \
{ \
StringName enum_name = __constant_get_enum_name(m_enum::m_member, #m_prefix "_" #m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \
_global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_BITFIELD_CLASS_FLAG(m_enum, m_prefix, m_member) \
_global_constants.push_back(_CoreConstant(__constant_get_bitfield_name(m_enum::m_member, #m_prefix "_" #m_member), #m_prefix "_" #m_member, (int64_t)m_enum::m_member, false, true));
#define BIND_CORE_BITFIELD_CLASS_FLAG(m_enum, m_prefix, m_member) \
{ \
StringName enum_name = __constant_get_bitfield_name(m_enum::m_member, #m_prefix "_" #m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member, false, true)); \
_global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(m_enum, m_name, m_member) \
_global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_enum::m_member, #m_name), #m_name, (int64_t)m_enum::m_member));
#define BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(m_enum, m_name, m_member) \
{ \
StringName enum_name = __constant_get_enum_name(m_enum::m_member, #m_name); \
_global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member)); \
_global_constants_map[#m_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(m_enum, m_name, m_member) \
_global_constants.push_back(_CoreConstant(__constant_get_bitfield_name(m_enum::m_member, #m_name), #m_name, (int64_t)m_enum::m_member, false, true));
#define BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(m_enum, m_name, m_member) \
{ \
StringName enum_name = __constant_get_bitfield_name(m_enum::m_member, #m_name); \
_global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member, false, true)); \
_global_constants_map[#m_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_ENUM_CLASS_CONSTANT_NO_VAL(m_enum, m_prefix, m_member) \
_global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_enum::m_member, #m_prefix "_" #m_member), #m_prefix "_" #m_member, (int64_t)m_enum::m_member, true));
#define BIND_CORE_ENUM_CLASS_CONSTANT_NO_VAL(m_enum, m_prefix, m_member) \
{ \
StringName enum_name = __constant_get_enum_name(m_enum::m_member, #m_prefix "_" #m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member, true)); \
_global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \
_global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_constant, #m_constant), m_custom_name, m_constant));
#define BIND_CORE_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \
{ \
StringName enum_name = __constant_get_enum_name(m_constant, #m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant)); \
_global_constants_map[m_custom_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_CONSTANT_NO_VAL(m_constant) \
_global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant, true));
#define BIND_CORE_CONSTANT_NO_VAL(m_constant) \
_global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant, true)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1;
#define BIND_CORE_ENUM_CONSTANT_NO_VAL(m_constant) \
_global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant, true));
#define BIND_CORE_ENUM_CONSTANT_NO_VAL(m_constant) \
{ \
StringName enum_name = __constant_get_enum_name(m_constant, #m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant, true)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant) \
_global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_constant, #m_constant), m_custom_name, m_constant, true));
#define BIND_CORE_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant) \
{ \
StringName enum_name = __constant_get_enum_name(m_constant, #m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant, true)); \
_global_constants_map[m_custom_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#else
#define BIND_CORE_CONSTANT(m_constant) \
_global_constants.push_back(_CoreConstant(#m_constant, m_constant));
#define BIND_CORE_CONSTANT(m_constant) \
_global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1;
#define BIND_CORE_ENUM_CONSTANT(m_constant) \
_global_constants.push_back(_CoreConstant(#m_constant, m_constant));
#define BIND_CORE_ENUM_CONSTANT(m_constant) \
{ \
StringName enum_name = __constant_get_enum_name(m_constant, #m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_BITFIELD_FLAG(m_constant) \
_global_constants.push_back(_CoreConstant(#m_constant, m_constant));
#define BIND_CORE_BITFIELD_FLAG(m_constant) \
{ \
StringName enum_name = __constant_get_bitfield_name(m_constant, #m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
// This just binds enum classes as if they were regular enum constants.
#define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member) \
_global_constants.push_back(_CoreConstant(#m_prefix "_" #m_member, (int64_t)m_enum::m_member));
#define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member) \
{ \
StringName enum_name = __constant_get_enum_name(m_enum::m_member, #m_prefix "_" #m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \
_global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_BITFIELD_CLASS_FLAG(m_enum, m_prefix, m_member) \
_global_constants.push_back(_CoreConstant(#m_prefix "_" #m_member, (int64_t)m_enum::m_member));
#define BIND_CORE_BITFIELD_CLASS_FLAG(m_enum, m_prefix, m_member) \
{ \
StringName enum_name = __constant_get_bitfield_name(m_enum::m_member, #m_prefix "_" #m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \
_global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(m_enum, m_name, m_member) \
_global_constants.push_back(_CoreConstant(#m_name, (int64_t)m_enum::m_member));
#define BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(m_enum, m_name, m_member) \
{ \
StringName enum_name = __constant_get_enum_name(m_enum::m_member, #m_name); \
_global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member)); \
_global_constants_map[#m_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(m_enum, m_name, m_member) \
_global_constants.push_back(_CoreConstant(#m_name, (int64_t)m_enum::m_member));
#define BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(m_enum, m_name, m_member) \
{ \
StringName enum_name = __constant_get_bitfield_name(m_enum::m_member, #m_name); \
_global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member)); \
_global_constants_map[#m_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_ENUM_CLASS_CONSTANT_NO_VAL(m_enum, m_prefix, m_member) \
_global_constants.push_back(_CoreConstant(#m_prefix "_" #m_member, (int64_t)m_enum::m_member));
#define BIND_CORE_ENUM_CLASS_CONSTANT_NO_VAL(m_enum, m_prefix, m_member) \
{ \
StringName enum_name = __constant_get_enum_name(m_enum::m_member, #m_prefix "_" #m_member); \
_global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \
_global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \
_global_constants.push_back(_CoreConstant(m_custom_name, m_constant));
#define BIND_CORE_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \
{ \
StringName enum_name = __constant_get_enum_name(m_constant, #m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant)); \
_global_constants_map[m_custom_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_CONSTANT_NO_VAL(m_constant) \
_global_constants.push_back(_CoreConstant(#m_constant, m_constant));
#define BIND_CORE_CONSTANT_NO_VAL(m_constant) \
_global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1;
#define BIND_CORE_ENUM_CONSTANT_NO_VAL(m_constant) \
_global_constants.push_back(_CoreConstant(#m_constant, m_constant));
#define BIND_CORE_ENUM_CONSTANT_NO_VAL(m_constant) \
{ \
StringName enum_name = __constant_get_enum_name(m_constant, #m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant)); \
_global_constants_map[#m_constant] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#define BIND_CORE_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant) \
_global_constants.push_back(_CoreConstant(m_custom_name, m_constant));
#define BIND_CORE_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant) \
{ \
StringName enum_name = __constant_get_enum_name(m_constant, #m_constant); \
_global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant)); \
_global_constants_map[m_custom_name] = _global_constants.size() - 1; \
_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \
}
#endif
@ -690,11 +798,11 @@ int CoreConstants::get_global_constant_count() {
return _global_constants.size();
}
#ifdef DEBUG_METHODS_ENABLED
StringName CoreConstants::get_global_constant_enum(int p_idx) {
return _global_constants[p_idx].enum_name;
}
#ifdef DEBUG_METHODS_ENABLED
bool CoreConstants::is_global_constant_bitfield(int p_idx) {
return _global_constants[p_idx].is_bitfield;
}
@ -703,10 +811,6 @@ bool CoreConstants::get_ignore_value_in_docs(int p_idx) {
return _global_constants[p_idx].ignore_value_in_docs;
}
#else
StringName CoreConstants::get_global_constant_enum(int p_idx) {
return StringName();
}
bool CoreConstants::is_global_constant_bitfield(int p_idx) {
return false;
}
@ -723,3 +827,24 @@ const char *CoreConstants::get_global_constant_name(int p_idx) {
int64_t CoreConstants::get_global_constant_value(int p_idx) {
return _global_constants[p_idx].value;
}
bool CoreConstants::is_global_constant(const StringName &p_name) {
return _global_constants_map.has(p_name);
}
int CoreConstants::get_global_constant_index(const StringName &p_name) {
ERR_FAIL_COND_V_MSG(!_global_constants_map.has(p_name), -1, "Trying to get index of non-existing constant.");
return _global_constants_map[p_name];
}
bool CoreConstants::is_global_enum(const StringName &p_enum) {
return _global_enums.has(p_enum);
}
void CoreConstants::get_enum_values(StringName p_enum, HashMap<StringName, int64_t> *p_values) {
ERR_FAIL_NULL_MSG(p_values, "Trying to get enum values with null map.");
ERR_FAIL_COND_MSG(!_global_enums.has(p_enum), "Trying to get values of non-existing enum.");
for (const _CoreConstant &constant : _global_enums[p_enum]) {
(*p_values)[constant.name] = constant.value;
}
}

View File

@ -32,6 +32,7 @@
#define CORE_CONSTANTS_H
#include "core/string/string_name.h"
#include "core/templates/hash_set.h"
class CoreConstants {
public:
@ -41,6 +42,10 @@ public:
static bool get_ignore_value_in_docs(int p_idx);
static const char *get_global_constant_name(int p_idx);
static int64_t get_global_constant_value(int p_idx);
static bool is_global_constant(const StringName &p_name);
static int get_global_constant_index(const StringName &p_name);
static bool is_global_enum(const StringName &p_enum);
static void get_enum_values(StringName p_enum, HashMap<StringName, int64_t> *p_values);
};
#endif // CORE_CONSTANTS_H

View File

@ -32,6 +32,7 @@
#include "core/config/engine.h"
#include "core/config/project_settings.h"
#include "core/core_constants.h"
#include "core/core_string_names.h"
#include "core/io/file_access.h"
#include "core/io/resource_loader.h"
@ -48,7 +49,7 @@
#endif
#define UNNAMED_ENUM "<anonymous enum>"
#define ENUM_SEPARATOR "::"
#define ENUM_SEPARATOR "."
static MethodInfo info_from_utility_func(const StringName &p_function) {
ERR_FAIL_COND_V(!Variant::has_utility_function(p_function), MethodInfo());
@ -137,12 +138,16 @@ static GDScriptParser::DataType make_enum_type(const StringName &p_enum_name, co
// For enums, native_type is only used to check compatibility in is_type_compatible()
// We can set anything readable here for error messages, as long as it uniquely identifies the type of the enum
type.native_type = p_base_name + ENUM_SEPARATOR + p_enum_name;
if (p_base_name.is_empty()) {
type.native_type = p_enum_name;
} else {
type.native_type = p_base_name + ENUM_SEPARATOR + p_enum_name;
}
return type;
}
static GDScriptParser::DataType make_native_enum_type(const StringName &p_enum_name, const StringName &p_native_class, const bool p_meta = true) {
static GDScriptParser::DataType make_native_enum_type(const StringName &p_enum_name, const StringName &p_native_class, bool p_meta = true) {
// Find out which base class declared the enum, so the name is always the same even when coming from other contexts.
StringName native_base = p_native_class;
while (true && native_base != StringName()) {
@ -154,7 +159,7 @@ static GDScriptParser::DataType make_native_enum_type(const StringName &p_enum_n
GDScriptParser::DataType type = make_enum_type(p_enum_name, native_base, p_meta);
if (p_meta) {
type.builtin_type = Variant::NIL; // Native enum types are not Dictionaries
type.builtin_type = Variant::NIL; // Native enum types are not Dictionaries.
}
List<StringName> enum_values;
@ -167,6 +172,22 @@ static GDScriptParser::DataType make_native_enum_type(const StringName &p_enum_n
return type;
}
static GDScriptParser::DataType make_global_enum_type(const StringName &p_enum_name, const StringName &p_base, bool p_meta = true) {
GDScriptParser::DataType type = make_enum_type(p_enum_name, p_base, p_meta);
if (p_meta) {
type.builtin_type = Variant::NIL; // Native enum types are not Dictionaries.
type.is_pseudo_type = true;
}
HashMap<StringName, int64_t> enum_values;
CoreConstants::get_enum_values(type.native_type, &enum_values);
for (const KeyValue<StringName, int64_t> &element : enum_values) {
type.enum_values[element.key] = element.value;
}
return type;
}
static GDScriptParser::DataType make_builtin_meta_type(Variant::Type p_type) {
GDScriptParser::DataType type;
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
@ -574,9 +595,19 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
StringName first = p_type->type_chain[0]->name;
if (first == SNAME("Variant")) {
if (p_type->type_chain.size() > 1) {
// TODO: Variant does actually have a nested Type though.
push_error(R"(Variant doesn't contain nested types.)", p_type->type_chain[1]);
if (p_type->type_chain.size() == 2) {
// May be nested enum.
StringName enum_name = p_type->type_chain[1]->name;
StringName qualified_name = String(first) + ENUM_SEPARATOR + String(p_type->type_chain[1]->name);
if (CoreConstants::is_global_enum(qualified_name)) {
result = make_global_enum_type(enum_name, first, true);
return result;
} else {
push_error(vformat(R"(Name "%s" is not a nested type of "Variant".)", enum_name), p_type->type_chain[1]);
return bad_type;
}
} else if (p_type->type_chain.size() > 2) {
push_error(R"(Variant only contains enum types, which do not have nested types.)", p_type->type_chain[2]);
return bad_type;
}
result.kind = GDScriptParser::DataType::VARIANT;
@ -634,6 +665,12 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
} else if (ClassDB::has_enum(parser->current_class->base_type.native_type, first)) {
// Native enum in current class.
result = make_native_enum_type(first, parser->current_class->base_type.native_type);
} else if (CoreConstants::is_global_enum(first)) {
if (p_type->type_chain.size() > 1) {
push_error(R"(Enums cannot contain nested types.)", p_type->type_chain[1]);
return bad_type;
}
result = make_global_enum_type(first, StringName());
} else {
// Classes in current scope.
List<GDScriptParser::ClassNode *> script_classes;
@ -3633,6 +3670,20 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
}
}
if (CoreConstants::is_global_constant(name)) {
int index = CoreConstants::get_global_constant_index(name);
StringName enum_name = CoreConstants::get_global_constant_enum(index);
int64_t value = CoreConstants::get_global_constant_value(index);
if (enum_name != StringName()) {
p_identifier->set_datatype(make_global_enum_type(enum_name, StringName(), false));
} else {
p_identifier->set_datatype(type_from_variant(value, p_identifier));
}
p_identifier->is_constant = true;
p_identifier->reduced_value = value;
return;
}
if (GDScriptLanguage::get_singleton()->has_any_global_constant(name)) {
Variant constant = GDScriptLanguage::get_singleton()->get_any_global_constant(name);
p_identifier->set_datatype(type_from_variant(constant, p_identifier));
@ -3641,6 +3692,25 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
return;
}
if (CoreConstants::is_global_enum(name)) {
p_identifier->set_datatype(make_global_enum_type(name, StringName(), true));
if (!can_be_builtin) {
push_error(vformat(R"(Global enum "%s" cannot be used on its own.)", name), p_identifier);
}
return;
}
// Allow "Variant" here since it might be used for nested enums.
if (can_be_builtin && name == SNAME("Variant")) {
GDScriptParser::DataType variant;
variant.kind = GDScriptParser::DataType::VARIANT;
variant.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
variant.is_meta_type = true;
variant.is_pseudo_type = true;
p_identifier->set_datatype(variant);
return;
}
// Not found.
// Check if it's a builtin function.
if (GDScriptUtilityFunctions::function_exists(name)) {
@ -3781,12 +3851,14 @@ void GDScriptAnalyzer::reduce_self(GDScriptParser::SelfNode *p_self) {
mark_lambda_use_self();
}
void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscript) {
void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscript, bool p_can_be_pseudo_type) {
if (p_subscript->base == nullptr) {
return;
}
if (p_subscript->base->type == GDScriptParser::Node::IDENTIFIER) {
reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base), true);
} else if (p_subscript->base->type == GDScriptParser::Node::SUBSCRIPT) {
reduce_subscript(static_cast<GDScriptParser::SubscriptNode *>(p_subscript->base), true);
} else {
reduce_expression(p_subscript->base);
}
@ -3810,14 +3882,28 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
result_type = type_from_variant(value, p_subscript);
}
} else if (base_type.is_variant() || !base_type.is_hard_type()) {
valid = true;
valid = !base_type.is_pseudo_type || p_can_be_pseudo_type;
result_type.kind = GDScriptParser::DataType::VARIANT;
mark_node_unsafe(p_subscript);
if (base_type.is_variant() && base_type.is_hard_type() && base_type.is_meta_type && base_type.is_pseudo_type) {
// Special case: it may be a global enum with pseudo base (e.g. Variant.Type).
String enum_name;
if (p_subscript->base->type == GDScriptParser::Node::IDENTIFIER) {
enum_name = String(static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base)->name) + ENUM_SEPARATOR + String(p_subscript->attribute->name);
}
if (CoreConstants::is_global_enum(enum_name)) {
result_type = make_global_enum_type(enum_name, StringName());
} else {
valid = false;
mark_node_unsafe(p_subscript);
}
} else {
mark_node_unsafe(p_subscript);
}
} else {
reduce_identifier_from_base(p_subscript->attribute, &base_type);
GDScriptParser::DataType attr_type = p_subscript->attribute->get_datatype();
if (attr_type.is_set()) {
valid = true;
valid = !attr_type.is_pseudo_type || p_can_be_pseudo_type;
result_type = attr_type;
p_subscript->is_constant = p_subscript->attribute->is_constant;
p_subscript->reduced_value = p_subscript->attribute->reduced_value;
@ -3833,7 +3919,12 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
}
}
if (!valid) {
push_error(vformat(R"(Cannot find member "%s" in base "%s".)", p_subscript->attribute->name, type_from_metatype(base_type).to_string()), p_subscript->attribute);
GDScriptParser::DataType attr_type = p_subscript->attribute->get_datatype();
if (!p_can_be_pseudo_type && (attr_type.is_pseudo_type || result_type.is_pseudo_type)) {
push_error(vformat(R"(Type "%s" in base "%s" cannot be used on its own.)", p_subscript->attribute->name, type_from_metatype(base_type).to_string()), p_subscript->attribute);
} else {
push_error(vformat(R"(Cannot find member "%s" in base "%s".)", p_subscript->attribute->name, type_from_metatype(base_type).to_string()), p_subscript->attribute);
}
result_type.kind = GDScriptParser::DataType::VARIANT;
}
} else {
@ -4389,6 +4480,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
GDScriptParser::DataType GDScriptAnalyzer::type_from_metatype(const GDScriptParser::DataType &p_meta_type) {
GDScriptParser::DataType result = p_meta_type;
result.is_meta_type = false;
result.is_pseudo_type = false;
if (p_meta_type.kind == GDScriptParser::DataType::ENUM) {
result.builtin_type = Variant::INT;
} else {
@ -4442,11 +4534,16 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo
result.set_container_element_type(elem_type);
} else if (p_property.type == Variant::INT) {
// Check if it's enum.
if (p_property.class_name != StringName()) {
Vector<String> names = String(p_property.class_name).split(".");
if (names.size() == 2) {
result = make_native_enum_type(names[1], names[0], false);
if ((p_property.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) && p_property.class_name != StringName()) {
if (CoreConstants::is_global_enum(p_property.class_name)) {
result = make_global_enum_type(p_property.class_name, StringName(), false);
result.is_constant = false;
} else {
Vector<String> names = String(p_property.class_name).split(ENUM_SEPARATOR);
if (names.size() == 2) {
result = make_native_enum_type(names[1], names[0], false);
result.is_constant = false;
}
}
}
}

View File

@ -98,7 +98,7 @@ class GDScriptAnalyzer {
void reduce_literal(GDScriptParser::LiteralNode *p_literal);
void reduce_preload(GDScriptParser::PreloadNode *p_preload);
void reduce_self(GDScriptParser::SelfNode *p_self);
void reduce_subscript(GDScriptParser::SubscriptNode *p_subscript);
void reduce_subscript(GDScriptParser::SubscriptNode *p_subscript, bool p_can_be_pseudo_type = false);
void reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op, bool p_is_root = false);
void reduce_type_test(GDScriptParser::TypeTestNode *p_type_test);
void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op);

View File

@ -125,6 +125,7 @@ public:
bool is_constant = false;
bool is_read_only = false;
bool is_meta_type = false;
bool is_pseudo_type = false; // For global names that can't be used standalone.
bool is_coroutine = false; // For function calls.
Variant::Type builtin_type = Variant::NIL;
@ -211,6 +212,7 @@ public:
is_read_only = p_other.is_read_only;
is_constant = p_other.is_constant;
is_meta_type = p_other.is_meta_type;
is_pseudo_type = p_other.is_pseudo_type;
is_coroutine = p_other.is_coroutine;
builtin_type = p_other.builtin_type;
native_type = p_other.native_type;

View File

@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
Cannot find member "V3" in base "enum_bad_value.gd::Enum".
Cannot find member "V3" in base "enum_bad_value.gd.Enum".

View File

@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
Cannot assign a value of type "enum_class_var_assign_with_wrong_enum_type.gd::MyOtherEnum" as "enum_class_var_assign_with_wrong_enum_type.gd::MyEnum".
Cannot assign a value of type "enum_class_var_assign_with_wrong_enum_type.gd.MyOtherEnum" as "enum_class_var_assign_with_wrong_enum_type.gd.MyEnum".

View File

@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
Cannot assign a value of type "enum_class_var_init_with_wrong_enum_type.gd::MyOtherEnum" as "enum_class_var_init_with_wrong_enum_type.gd::MyEnum".
Cannot assign a value of type "enum_class_var_init_with_wrong_enum_type.gd.MyOtherEnum" as "enum_class_var_init_with_wrong_enum_type.gd.MyEnum".

View File

@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
Cannot pass a value of type "enum_function_parameter_wrong_type.gd::MyOtherEnum" as "enum_function_parameter_wrong_type.gd::MyEnum".
Cannot pass a value of type "enum_function_parameter_wrong_type.gd.MyOtherEnum" as "enum_function_parameter_wrong_type.gd.MyEnum".

View File

@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
Cannot return a value of type "enum_function_return_wrong_type.gd::MyOtherEnum" as "enum_function_return_wrong_type.gd::MyEnum".
Cannot return a value of type "enum_function_return_wrong_type.gd.MyOtherEnum" as "enum_function_return_wrong_type.gd.MyEnum".

View File

@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
Cannot assign a value of type "enum_local_var_assign_outer_with_wrong_enum_type.gd::InnerClass::MyEnum" as "enum_local_var_assign_outer_with_wrong_enum_type.gd::MyEnum".
Cannot assign a value of type "enum_local_var_assign_outer_with_wrong_enum_type.gd::InnerClass.MyEnum" as "enum_local_var_assign_outer_with_wrong_enum_type.gd.MyEnum".

View File

@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
Cannot assign a value of type "enum_local_var_assign_with_wrong_enum_type.gd::MyOtherEnum" as "enum_local_var_assign_with_wrong_enum_type.gd::MyEnum".
Cannot assign a value of type "enum_local_var_assign_with_wrong_enum_type.gd.MyOtherEnum" as "enum_local_var_assign_with_wrong_enum_type.gd.MyEnum".

View File

@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
Cannot assign a value of type "enum_local_var_init_with_wrong_enum_type.gd::MyOtherEnum" as "enum_local_var_init_with_wrong_enum_type.gd::MyEnum".
Cannot assign a value of type "enum_local_var_init_with_wrong_enum_type.gd.MyOtherEnum" as "enum_local_var_init_with_wrong_enum_type.gd.MyEnum".

View File

@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
Cannot find member "THIS_DOES_NOT_EXIST" in base "TileSet::TileShape".
Cannot find member "THIS_DOES_NOT_EXIST" in base "TileSet.TileShape".

View File

@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
Cannot assign a value of type "enum_value_from_parent.gd::<anonymous enum>" as "enum_preload_unnamed_assign_to_named.gd::MyEnum".
Cannot assign a value of type "enum_value_from_parent.gd.<anonymous enum>" as "enum_preload_unnamed_assign_to_named.gd.MyEnum".

View File

@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
Cannot assign a value of type "enum_unnamed_assign_to_named.gd::<anonymous enum>" as "enum_unnamed_assign_to_named.gd::MyEnum".
Cannot assign a value of type "enum_unnamed_assign_to_named.gd.<anonymous enum>" as "enum_unnamed_assign_to_named.gd.MyEnum".

View File

@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
Cannot assign a value of type "enum_from_outer.gd::Named" as "preload_enum_error.gd::LocalNamed".
Cannot assign a value of type "enum_from_outer.gd.Named" as "preload_enum_error.gd.LocalNamed".

View File

@ -0,0 +1,30 @@
func test():
var type: Variant.Type
type = Variant.Type.TYPE_INT
print(type)
type = TYPE_FLOAT
print(type)
var direction: ClockDirection
direction = ClockDirection.CLOCKWISE
print(direction)
direction = COUNTERCLOCKWISE
print(direction)
var duper := Duper.new()
duper.set_type(Variant.Type.TYPE_INT)
duper.set_type(TYPE_FLOAT)
duper.set_direction(ClockDirection.CLOCKWISE)
duper.set_direction(COUNTERCLOCKWISE)
class Super:
func set_type(type: Variant.Type) -> void:
print(type)
func set_direction(dir: ClockDirection) -> void:
print(dir)
class Duper extends Super:
func set_type(type: Variant.Type) -> void:
print(type)
func set_direction(dir: ClockDirection) -> void:
print(dir)

View File

@ -0,0 +1,9 @@
GDTEST_OK
2
3
0
1
2
3
0
1

View File

@ -2,5 +2,5 @@ GDTEST_OK
>> WARNING
>> Line: 5
>> INT_AS_ENUM_WITHOUT_MATCH
>> Cannot cast 2 as Enum "cast_enum_bad_enum.gd::MyEnum": no enum member has matching value.
>> Cannot cast 2 as Enum "cast_enum_bad_enum.gd.MyEnum": no enum member has matching value.
2

View File

@ -2,5 +2,5 @@ GDTEST_OK
>> WARNING
>> Line: 4
>> INT_AS_ENUM_WITHOUT_MATCH
>> Cannot cast 2 as Enum "cast_enum_bad_int.gd::MyEnum": no enum member has matching value.
>> Cannot cast 2 as Enum "cast_enum_bad_int.gd.MyEnum": no enum member has matching value.
2