Kaynağa Gözat

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

Make global scope enums accessible as types in GDScript
Rémi Verschelde 2 yıl önce
ebeveyn
işleme
2057d7344e
21 değiştirilmiş dosya ile 355 ekleme ve 87 silme
  1. 181 56
      core/core_constants.cpp
  2. 5 0
      core/core_constants.h
  3. 113 16
      modules/gdscript/gdscript_analyzer.cpp
  4. 1 1
      modules/gdscript/gdscript_analyzer.h
  5. 2 0
      modules/gdscript/gdscript_parser.h
  6. 1 1
      modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.out
  7. 1 1
      modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out
  8. 1 1
      modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out
  9. 1 1
      modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out
  10. 1 1
      modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out
  11. 1 1
      modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out
  12. 1 1
      modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out
  13. 1 1
      modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out
  14. 1 1
      modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.out
  15. 1 1
      modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out
  16. 1 1
      modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out
  17. 1 1
      modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out
  18. 30 0
      modules/gdscript/tests/scripts/analyzer/features/global_enums.gd
  19. 9 0
      modules/gdscript/tests/scripts/analyzer/features/global_enums.out
  20. 1 1
      modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_enum.out
  21. 1 1
      modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_int.out

+ 181 - 56
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;
+	}
+}

+ 5 - 0
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

+ 113 - 16
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;
+					}
 				}
 			}
 		}

+ 1 - 1
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);

+ 2 - 0
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;

+ 1 - 1
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".

+ 1 - 1
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".

+ 1 - 1
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".

+ 1 - 1
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".

+ 1 - 1
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".

+ 1 - 1
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".

+ 1 - 1
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".

+ 1 - 1
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".

+ 1 - 1
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".

+ 1 - 1
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".

+ 1 - 1
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".

+ 1 - 1
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".

+ 30 - 0
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)

+ 9 - 0
modules/gdscript/tests/scripts/analyzer/features/global_enums.out

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

+ 1 - 1
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

+ 1 - 1
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