summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRémi Verschelde <rverschelde@gmail.com>2023-02-20 15:41:45 +0100
committerRémi Verschelde <rverschelde@gmail.com>2023-02-20 15:41:45 +0100
commit2057d7344ee60fc58d7f09ff742dea1850291d21 (patch)
treec80dfa68a6d7d9718b64970a171c0769aa27fed9
parentea2bc9795c46ebb705c72add7a30ffeb59ff7922 (diff)
parent75f16b8167fe23d06983d92a9bb66701cf0a0f7a (diff)
Merge pull request #73590 from vnen/gdscript-global-scope-enums
Make global scope enums accessible as types in GDScript
-rw-r--r--core/core_constants.cpp237
-rw-r--r--core/core_constants.h5
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp129
-rw-r--r--modules/gdscript/gdscript_analyzer.h2
-rw-r--r--modules/gdscript/gdscript_parser.h2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/global_enums.gd30
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/global_enums.out9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_enum.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_int.out2
21 files changed, 355 insertions, 87 deletions
diff --git a/core/core_constants.cpp b/core/core_constants.cpp
index b1f56539e5..d88dda6609 100644
--- a/core/core_constants.cpp
+++ b/core/core_constants.cpp
@@ -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;
+ }
+}
diff --git a/core/core_constants.h b/core/core_constants.h
index 5a5cd4394c..51842490c8 100644
--- a/core/core_constants.h
+++ b/core/core_constants.h
@@ -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
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 046158e24e..38f9163f70 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -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;
+ }
}
}
}
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index 22004b899f..7a50b32d4c 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -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);
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 7d50910e5d..346c9bc45d 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -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;
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.out
index ddbdc17a42..1b6e11f6f2 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.out
@@ -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".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out
index 84958f1aa2..d401675bcf 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out
@@ -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".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out
index e294f3496a..4b6b42b024 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out
@@ -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".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out
index a91189e2dd..adde630a0b 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out
@@ -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".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out
index 6b4eba3740..9ee3fb7c06 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out
@@ -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".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out
index 616358bb61..8de7bde50d 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out
@@ -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".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out
index af3dde663f..e2139e75f6 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out
@@ -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".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out
index 781b0bc85f..46c0553c28 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out
@@ -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".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.out
index 49f041a2dd..4b1db77a35 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.out
@@ -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".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out
index e8c7f86c4f..ddd26e7399 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out
@@ -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".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out
index fb18c94d0b..3a65978aa3 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out
@@ -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".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out b/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out
index 08a973503f..21143f2ade 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out
@@ -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".
diff --git a/modules/gdscript/tests/scripts/analyzer/features/global_enums.gd b/modules/gdscript/tests/scripts/analyzer/features/global_enums.gd
new file mode 100644
index 0000000000..67d48817e8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/global_enums.gd
@@ -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)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/global_enums.out b/modules/gdscript/tests/scripts/analyzer/features/global_enums.out
new file mode 100644
index 0000000000..b3b5bf57c1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/global_enums.out
@@ -0,0 +1,9 @@
+GDTEST_OK
+2
+3
+0
+1
+2
+3
+0
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_enum.out b/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_enum.out
index 6e086a0918..6d8aeaf0b6 100644
--- a/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_enum.out
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_enum.out
@@ -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
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_int.out b/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_int.out
index c19d57f98e..b0e4af29a0 100644
--- a/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_int.out
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_int.out
@@ -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