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

Make global scope enums accessible as types in GDScript

Add functions to CoreConstant so enums can be properly deduced. Also add
the enums in release builds to make consistent with ClassDB enums and
avoid differences in script compilation between debug and release.
This commit is contained in:
George Marques
2023-02-19 12:57:09 -03:00
parent 28db611f0f
commit 75f16b8167
21 changed files with 355 additions and 87 deletions

View File

@@ -33,14 +33,15 @@
#include "core/input/input_event.h" #include "core/input/input_event.h"
#include "core/object/class_db.h" #include "core/object/class_db.h"
#include "core/os/keyboard.h" #include "core/os/keyboard.h"
#include "core/templates/hash_set.h"
#include "core/variant/variant.h" #include "core/variant/variant.h"
struct _CoreConstant { struct _CoreConstant {
#ifdef DEBUG_METHODS_ENABLED #ifdef DEBUG_METHODS_ENABLED
StringName enum_name;
bool ignore_value_in_docs = false; bool ignore_value_in_docs = false;
bool is_bitfield = false; bool is_bitfield = false;
#endif #endif
StringName enum_name;
const char *name = nullptr; const char *name = nullptr;
int64_t value = 0; int64_t value = 0;
@@ -48,14 +49,15 @@ struct _CoreConstant {
#ifdef DEBUG_METHODS_ENABLED #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) : _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), ignore_value_in_docs(p_ignore_value_in_docs),
is_bitfield(p_is_bitfield), is_bitfield(p_is_bitfield),
enum_name(p_enum_name),
name(p_name), name(p_name),
value(p_value) { value(p_value) {
} }
#else #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), name(p_name),
value(p_value) { value(p_value) {
} }
@@ -63,84 +65,190 @@ struct _CoreConstant {
}; };
static Vector<_CoreConstant> _global_constants; static Vector<_CoreConstant> _global_constants;
static HashMap<StringName, int> _global_constants_map;
static HashMap<StringName, Vector<_CoreConstant>> _global_enums;
#ifdef DEBUG_METHODS_ENABLED #ifdef DEBUG_METHODS_ENABLED
#define BIND_CORE_CONSTANT(m_constant) \ #define BIND_CORE_CONSTANT(m_constant) \
_global_constants.push_back(_CoreConstant(StringName(), #m_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) \ #define BIND_CORE_ENUM_CONSTANT(m_constant) \
_global_constants.push_back(_CoreConstant(__constant_get_enum_name(m_constant, #m_constant), #m_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) \ #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)); { \
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. // This just binds enum classes as if they were regular enum constants.
#define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member) \ #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)); { \
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) \ #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)); { \
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) \ #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)); { \
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) \ #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)); { \
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) \ #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)); { \
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) \ #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)); { \
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) \ #define BIND_CORE_CONSTANT_NO_VAL(m_constant) \
_global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant, true)); _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) \ #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)); { \
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) \ #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)); { \
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 #else
#define BIND_CORE_CONSTANT(m_constant) \ #define BIND_CORE_CONSTANT(m_constant) \
_global_constants.push_back(_CoreConstant(#m_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) \ #define BIND_CORE_ENUM_CONSTANT(m_constant) \
_global_constants.push_back(_CoreConstant(#m_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) \ #define BIND_CORE_BITFIELD_FLAG(m_constant) \
_global_constants.push_back(_CoreConstant(#m_constant, 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. // This just binds enum classes as if they were regular enum constants.
#define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member) \ #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)); { \
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) \ #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)); { \
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) \ #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)); { \
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) \ #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)); { \
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) \ #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)); { \
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) \ #define BIND_CORE_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant) \
_global_constants.push_back(_CoreConstant(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) \ #define BIND_CORE_CONSTANT_NO_VAL(m_constant) \
_global_constants.push_back(_CoreConstant(#m_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_NO_VAL(m_constant) \ #define BIND_CORE_ENUM_CONSTANT_NO_VAL(m_constant) \
_global_constants.push_back(_CoreConstant(#m_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_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant) \ #define BIND_CORE_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant) \
_global_constants.push_back(_CoreConstant(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 #endif
@@ -690,11 +798,11 @@ int CoreConstants::get_global_constant_count() {
return _global_constants.size(); return _global_constants.size();
} }
#ifdef DEBUG_METHODS_ENABLED
StringName CoreConstants::get_global_constant_enum(int p_idx) { StringName CoreConstants::get_global_constant_enum(int p_idx) {
return _global_constants[p_idx].enum_name; return _global_constants[p_idx].enum_name;
} }
#ifdef DEBUG_METHODS_ENABLED
bool CoreConstants::is_global_constant_bitfield(int p_idx) { bool CoreConstants::is_global_constant_bitfield(int p_idx) {
return _global_constants[p_idx].is_bitfield; 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; return _global_constants[p_idx].ignore_value_in_docs;
} }
#else #else
StringName CoreConstants::get_global_constant_enum(int p_idx) {
return StringName();
}
bool CoreConstants::is_global_constant_bitfield(int p_idx) { bool CoreConstants::is_global_constant_bitfield(int p_idx) {
return false; 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) { int64_t CoreConstants::get_global_constant_value(int p_idx) {
return _global_constants[p_idx].value; 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 #define CORE_CONSTANTS_H
#include "core/string/string_name.h" #include "core/string/string_name.h"
#include "core/templates/hash_set.h"
class CoreConstants { class CoreConstants {
public: public:
@@ -41,6 +42,10 @@ public:
static bool get_ignore_value_in_docs(int p_idx); static bool get_ignore_value_in_docs(int p_idx);
static const char *get_global_constant_name(int p_idx); static const char *get_global_constant_name(int p_idx);
static int64_t get_global_constant_value(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 #endif // CORE_CONSTANTS_H

View File

@@ -32,6 +32,7 @@
#include "core/config/engine.h" #include "core/config/engine.h"
#include "core/config/project_settings.h" #include "core/config/project_settings.h"
#include "core/core_constants.h"
#include "core/core_string_names.h" #include "core/core_string_names.h"
#include "core/io/file_access.h" #include "core/io/file_access.h"
#include "core/io/resource_loader.h" #include "core/io/resource_loader.h"
@@ -48,7 +49,7 @@
#endif #endif
#define UNNAMED_ENUM "<anonymous enum>" #define UNNAMED_ENUM "<anonymous enum>"
#define ENUM_SEPARATOR "::" #define ENUM_SEPARATOR "."
static MethodInfo info_from_utility_func(const StringName &p_function) { static MethodInfo info_from_utility_func(const StringName &p_function) {
ERR_FAIL_COND_V(!Variant::has_utility_function(p_function), MethodInfo()); 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() // 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 // 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; 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. // 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; StringName native_base = p_native_class;
while (true && native_base != StringName()) { 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); GDScriptParser::DataType type = make_enum_type(p_enum_name, native_base, p_meta);
if (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; List<StringName> enum_values;
@@ -167,6 +172,22 @@ static GDScriptParser::DataType make_native_enum_type(const StringName &p_enum_n
return type; 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) { static GDScriptParser::DataType make_builtin_meta_type(Variant::Type p_type) {
GDScriptParser::DataType type; GDScriptParser::DataType type;
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
@@ -575,9 +596,19 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
StringName first = p_type->type_chain[0]->name; StringName first = p_type->type_chain[0]->name;
if (first == SNAME("Variant")) { if (first == SNAME("Variant")) {
if (p_type->type_chain.size() > 1) { if (p_type->type_chain.size() == 2) {
// TODO: Variant does actually have a nested Type though. // May be nested enum.
push_error(R"(Variant doesn't contain nested types.)", p_type->type_chain[1]); 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; return bad_type;
} }
result.kind = GDScriptParser::DataType::VARIANT; result.kind = GDScriptParser::DataType::VARIANT;
@@ -633,6 +664,12 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
} else if (ClassDB::has_enum(parser->current_class->base_type.native_type, first)) { } else if (ClassDB::has_enum(parser->current_class->base_type.native_type, first)) {
// Native enum in current class. // Native enum in current class.
result = make_native_enum_type(first, parser->current_class->base_type.native_type); 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 { } else {
// Classes in current scope. // Classes in current scope.
List<GDScriptParser::ClassNode *> script_classes; List<GDScriptParser::ClassNode *> script_classes;
@@ -3661,6 +3698,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)) { if (GDScriptLanguage::get_singleton()->has_any_global_constant(name)) {
Variant constant = GDScriptLanguage::get_singleton()->get_any_global_constant(name); Variant constant = GDScriptLanguage::get_singleton()->get_any_global_constant(name);
p_identifier->set_datatype(type_from_variant(constant, p_identifier)); p_identifier->set_datatype(type_from_variant(constant, p_identifier));
@@ -3669,6 +3720,25 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
return; 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. // Not found.
// Check if it's a builtin function. // Check if it's a builtin function.
if (GDScriptUtilityFunctions::function_exists(name)) { if (GDScriptUtilityFunctions::function_exists(name)) {
@@ -3809,12 +3879,14 @@ void GDScriptAnalyzer::reduce_self(GDScriptParser::SelfNode *p_self) {
mark_lambda_use_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) { if (p_subscript->base == nullptr) {
return; return;
} }
if (p_subscript->base->type == GDScriptParser::Node::IDENTIFIER) { if (p_subscript->base->type == GDScriptParser::Node::IDENTIFIER) {
reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base), true); 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 { } else {
reduce_expression(p_subscript->base); reduce_expression(p_subscript->base);
} }
@@ -3838,14 +3910,28 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
result_type = type_from_variant(value, p_subscript); result_type = type_from_variant(value, p_subscript);
} }
} else if (base_type.is_variant() || !base_type.is_hard_type()) { } 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; 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 { } else {
reduce_identifier_from_base(p_subscript->attribute, &base_type); reduce_identifier_from_base(p_subscript->attribute, &base_type);
GDScriptParser::DataType attr_type = p_subscript->attribute->get_datatype(); GDScriptParser::DataType attr_type = p_subscript->attribute->get_datatype();
if (attr_type.is_set()) { if (attr_type.is_set()) {
valid = true; valid = !attr_type.is_pseudo_type || p_can_be_pseudo_type;
result_type = attr_type; result_type = attr_type;
p_subscript->is_constant = p_subscript->attribute->is_constant; p_subscript->is_constant = p_subscript->attribute->is_constant;
p_subscript->reduced_value = p_subscript->attribute->reduced_value; p_subscript->reduced_value = p_subscript->attribute->reduced_value;
@@ -3861,7 +3947,12 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
} }
} }
if (!valid) { 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; result_type.kind = GDScriptParser::DataType::VARIANT;
} }
} else { } else {
@@ -4375,6 +4466,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 GDScriptAnalyzer::type_from_metatype(const GDScriptParser::DataType &p_meta_type) {
GDScriptParser::DataType result = p_meta_type; GDScriptParser::DataType result = p_meta_type;
result.is_meta_type = false; result.is_meta_type = false;
result.is_pseudo_type = false;
if (p_meta_type.kind == GDScriptParser::DataType::ENUM) { if (p_meta_type.kind == GDScriptParser::DataType::ENUM) {
result.builtin_type = Variant::INT; result.builtin_type = Variant::INT;
} else { } else {
@@ -4428,11 +4520,16 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo
result.set_container_element_type(elem_type); result.set_container_element_type(elem_type);
} else if (p_property.type == Variant::INT) { } else if (p_property.type == Variant::INT) {
// Check if it's enum. // Check if it's enum.
if (p_property.class_name != StringName()) { if ((p_property.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) && p_property.class_name != StringName()) {
Vector<String> names = String(p_property.class_name).split("."); if (CoreConstants::is_global_enum(p_property.class_name)) {
if (names.size() == 2) { result = make_global_enum_type(p_property.class_name, StringName(), false);
result = make_native_enum_type(names[1], names[0], false);
result.is_constant = 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_literal(GDScriptParser::LiteralNode *p_literal);
void reduce_preload(GDScriptParser::PreloadNode *p_preload); void reduce_preload(GDScriptParser::PreloadNode *p_preload);
void reduce_self(GDScriptParser::SelfNode *p_self); 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_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op, bool p_is_root = false);
void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op); void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op);

View File

@@ -124,6 +124,7 @@ public:
bool is_constant = false; bool is_constant = false;
bool is_read_only = false; bool is_read_only = false;
bool is_meta_type = 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. bool is_coroutine = false; // For function calls.
Variant::Type builtin_type = Variant::NIL; Variant::Type builtin_type = Variant::NIL;
@@ -210,6 +211,7 @@ public:
is_read_only = p_other.is_read_only; is_read_only = p_other.is_read_only;
is_constant = p_other.is_constant; is_constant = p_other.is_constant;
is_meta_type = p_other.is_meta_type; is_meta_type = p_other.is_meta_type;
is_pseudo_type = p_other.is_pseudo_type;
is_coroutine = p_other.is_coroutine; is_coroutine = p_other.is_coroutine;
builtin_type = p_other.builtin_type; builtin_type = p_other.builtin_type;
native_type = p_other.native_type; native_type = p_other.native_type;

View File

@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR 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 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 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 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 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 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 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 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 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 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 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 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 >> WARNING
>> Line: 5 >> Line: 5
>> INT_AS_ENUM_WITHOUT_MATCH >> 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 2

View File

@@ -2,5 +2,5 @@ GDTEST_OK
>> WARNING >> WARNING
>> Line: 4 >> Line: 4
>> INT_AS_ENUM_WITHOUT_MATCH >> 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 2