Browse Source

Merge pull request #79510 from dalexeev/gds-fix-const-non-metatype-subscript

GDScript: Fix subscript resolution for constant non-metatypes
Rémi Verschelde 1 year ago
parent
commit
971f678442

+ 1 - 1
modules/gdscript/gdscript_analyzer.cpp

@@ -4099,7 +4099,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
 		GDScriptParser::DataType base_type = p_subscript->base->get_datatype();
 		bool valid = false;
 		// If the base is a metatype, use the analyzer instead.
-		if (p_subscript->base->is_constant && !base_type.is_meta_type) {
+		if (p_subscript->base->is_constant && !base_type.is_meta_type && base_type.kind != GDScriptParser::DataType::CLASS) {
 			// Just try to get it.
 			Variant value = p_subscript->base->reduced_value.get_named(p_subscript->attribute->name, valid);
 			if (valid) {

+ 21 - 17
modules/gdscript/gdscript_parser.cpp

@@ -4095,25 +4095,29 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
 				}
 			} break;
 			case GDScriptParser::DataType::ENUM: {
-				variable->export_info.type = Variant::INT;
-				variable->export_info.hint = PROPERTY_HINT_ENUM;
-
-				String enum_hint_string;
-				bool first = true;
-				for (const KeyValue<StringName, int64_t> &E : export_type.enum_values) {
-					if (!first) {
-						enum_hint_string += ",";
-					} else {
-						first = false;
+				if (export_type.is_meta_type) {
+					variable->export_info.type = Variant::DICTIONARY;
+				} else {
+					variable->export_info.type = Variant::INT;
+					variable->export_info.hint = PROPERTY_HINT_ENUM;
+
+					String enum_hint_string;
+					bool first = true;
+					for (const KeyValue<StringName, int64_t> &E : export_type.enum_values) {
+						if (!first) {
+							enum_hint_string += ",";
+						} else {
+							first = false;
+						}
+						enum_hint_string += E.key.operator String().capitalize().xml_escape();
+						enum_hint_string += ":";
+						enum_hint_string += String::num_int64(E.value).xml_escape();
 					}
-					enum_hint_string += E.key.operator String().capitalize().xml_escape();
-					enum_hint_string += ":";
-					enum_hint_string += String::num_int64(E.value).xml_escape();
-				}
 
-				variable->export_info.hint_string = enum_hint_string;
-				variable->export_info.usage |= PROPERTY_USAGE_CLASS_IS_ENUM;
-				variable->export_info.class_name = String(export_type.native_type).replace("::", ".");
+					variable->export_info.hint_string = enum_hint_string;
+					variable->export_info.usage |= PROPERTY_USAGE_CLASS_IS_ENUM;
+					variable->export_info.class_name = String(export_type.native_type).replace("::", ".");
+				}
 			} break;
 			default:
 				push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", variable);

+ 17 - 0
modules/gdscript/tests/scripts/analyzer/features/export_enum_as_dictionary.gd

@@ -0,0 +1,17 @@
+class_name TestExportEnumAsDictionary
+
+enum MyEnum {A, B, C}
+
+const Utils = preload("../../utils.notest.gd")
+
+@export var x1 = MyEnum
+@export var x2 = MyEnum.A
+@export var x3 := MyEnum
+@export var x4 := MyEnum.A
+@export var x5: MyEnum
+
+func test():
+	for property in get_property_list():
+		if property.usage & PROPERTY_USAGE_SCRIPT_VARIABLE:
+			print(Utils.get_property_signature(property))
+			print("  ", Utils.get_property_additional_info(property))

+ 11 - 0
modules/gdscript/tests/scripts/analyzer/features/export_enum_as_dictionary.out

@@ -0,0 +1,11 @@
+GDTEST_OK
+@export var x1: Dictionary
+  hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE
+@export var x2: TestExportEnumAsDictionary.MyEnum
+  hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM
+@export var x3: Dictionary
+  hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE
+@export var x4: TestExportEnumAsDictionary.MyEnum
+  hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM
+@export var x5: TestExportEnumAsDictionary.MyEnum
+  hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM

+ 144 - 0
modules/gdscript/tests/scripts/utils.notest.gd

@@ -19,6 +19,7 @@ static func get_type(property: Dictionary, is_return: bool = false) -> String:
 				return property.class_name
 	return variant_get_type_name(property.type)
 
+
 static func get_property_signature(property: Dictionary, is_static: bool = false) -> String:
 	var result: String = ""
 	if not (property.usage & PROPERTY_USAGE_SCRIPT_VARIABLE):
@@ -30,6 +31,15 @@ static func get_property_signature(property: Dictionary, is_static: bool = false
 	result += "var " + property.name + ": " + get_type(property)
 	return result
 
+
+static func get_property_additional_info(property: Dictionary) -> String:
+	return 'hint=%s hint_string="%s" usage=%s' % [
+		get_property_hint_name(property.hint).trim_prefix("PROPERTY_HINT_"),
+		str(property.hint_string).c_escape(),
+		get_property_usage_string(property.usage).replace("PROPERTY_USAGE_", ""),
+	]
+
+
 static func get_method_signature(method: Dictionary, is_signal: bool = false) -> String:
 	var result: String = ""
 	if method.flags & METHOD_FLAG_STATIC:
@@ -55,6 +65,7 @@ static func get_method_signature(method: Dictionary, is_signal: bool = false) ->
 		result += " -> " + get_type(method.return, true)
 	return result
 
+
 static func variant_get_type_name(type: Variant.Type) -> String:
 	match type:
 		TYPE_NIL:
@@ -135,3 +146,136 @@ static func variant_get_type_name(type: Variant.Type) -> String:
 			return "PackedColorArray"
 	push_error("Argument `type` is invalid. Use `TYPE_*` constants.")
 	return "<invalid type>"
+
+
+static func get_property_hint_name(hint: PropertyHint) -> String:
+	match hint:
+		PROPERTY_HINT_NONE:
+			return "PROPERTY_HINT_NONE"
+		PROPERTY_HINT_RANGE:
+			return "PROPERTY_HINT_RANGE"
+		PROPERTY_HINT_ENUM:
+			return "PROPERTY_HINT_ENUM"
+		PROPERTY_HINT_ENUM_SUGGESTION:
+			return "PROPERTY_HINT_ENUM_SUGGESTION"
+		PROPERTY_HINT_EXP_EASING:
+			return "PROPERTY_HINT_EXP_EASING"
+		PROPERTY_HINT_LINK:
+			return "PROPERTY_HINT_LINK"
+		PROPERTY_HINT_FLAGS:
+			return "PROPERTY_HINT_FLAGS"
+		PROPERTY_HINT_LAYERS_2D_RENDER:
+			return "PROPERTY_HINT_LAYERS_2D_RENDER"
+		PROPERTY_HINT_LAYERS_2D_PHYSICS:
+			return "PROPERTY_HINT_LAYERS_2D_PHYSICS"
+		PROPERTY_HINT_LAYERS_2D_NAVIGATION:
+			return "PROPERTY_HINT_LAYERS_2D_NAVIGATION"
+		PROPERTY_HINT_LAYERS_3D_RENDER:
+			return "PROPERTY_HINT_LAYERS_3D_RENDER"
+		PROPERTY_HINT_LAYERS_3D_PHYSICS:
+			return "PROPERTY_HINT_LAYERS_3D_PHYSICS"
+		PROPERTY_HINT_LAYERS_3D_NAVIGATION:
+			return "PROPERTY_HINT_LAYERS_3D_NAVIGATION"
+		PROPERTY_HINT_LAYERS_AVOIDANCE:
+			return "PROPERTY_HINT_LAYERS_AVOIDANCE"
+		PROPERTY_HINT_FILE:
+			return "PROPERTY_HINT_FILE"
+		PROPERTY_HINT_DIR:
+			return "PROPERTY_HINT_DIR"
+		PROPERTY_HINT_GLOBAL_FILE:
+			return "PROPERTY_HINT_GLOBAL_FILE"
+		PROPERTY_HINT_GLOBAL_DIR:
+			return "PROPERTY_HINT_GLOBAL_DIR"
+		PROPERTY_HINT_RESOURCE_TYPE:
+			return "PROPERTY_HINT_RESOURCE_TYPE"
+		PROPERTY_HINT_MULTILINE_TEXT:
+			return "PROPERTY_HINT_MULTILINE_TEXT"
+		PROPERTY_HINT_EXPRESSION:
+			return "PROPERTY_HINT_EXPRESSION"
+		PROPERTY_HINT_PLACEHOLDER_TEXT:
+			return "PROPERTY_HINT_PLACEHOLDER_TEXT"
+		PROPERTY_HINT_COLOR_NO_ALPHA:
+			return "PROPERTY_HINT_COLOR_NO_ALPHA"
+		PROPERTY_HINT_OBJECT_ID:
+			return "PROPERTY_HINT_OBJECT_ID"
+		PROPERTY_HINT_TYPE_STRING:
+			return "PROPERTY_HINT_TYPE_STRING"
+		PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE:
+			return "PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE"
+		PROPERTY_HINT_OBJECT_TOO_BIG:
+			return "PROPERTY_HINT_OBJECT_TOO_BIG"
+		PROPERTY_HINT_NODE_PATH_VALID_TYPES:
+			return "PROPERTY_HINT_NODE_PATH_VALID_TYPES"
+		PROPERTY_HINT_SAVE_FILE:
+			return "PROPERTY_HINT_SAVE_FILE"
+		PROPERTY_HINT_GLOBAL_SAVE_FILE:
+			return "PROPERTY_HINT_GLOBAL_SAVE_FILE"
+		PROPERTY_HINT_INT_IS_OBJECTID:
+			return "PROPERTY_HINT_INT_IS_OBJECTID"
+		PROPERTY_HINT_INT_IS_POINTER:
+			return "PROPERTY_HINT_INT_IS_POINTER"
+		PROPERTY_HINT_ARRAY_TYPE:
+			return "PROPERTY_HINT_ARRAY_TYPE"
+		PROPERTY_HINT_LOCALE_ID:
+			return "PROPERTY_HINT_LOCALE_ID"
+		PROPERTY_HINT_LOCALIZABLE_STRING:
+			return "PROPERTY_HINT_LOCALIZABLE_STRING"
+		PROPERTY_HINT_NODE_TYPE:
+			return "PROPERTY_HINT_NODE_TYPE"
+		PROPERTY_HINT_HIDE_QUATERNION_EDIT:
+			return "PROPERTY_HINT_HIDE_QUATERNION_EDIT"
+		PROPERTY_HINT_PASSWORD:
+			return "PROPERTY_HINT_PASSWORD"
+	push_error("Argument `hint` is invalid. Use `PROPERTY_HINT_*` constants.")
+	return "<invalid hint>"
+
+
+static func get_property_usage_string(usage: int) -> String:
+	if usage == PROPERTY_USAGE_NONE:
+		return "PROPERTY_USAGE_NONE"
+
+	const FLAGS: Array[Array] = [
+		[PROPERTY_USAGE_DEFAULT, "PROPERTY_USAGE_DEFAULT"],
+		[PROPERTY_USAGE_STORAGE, "PROPERTY_USAGE_STORAGE"],
+		[PROPERTY_USAGE_EDITOR, "PROPERTY_USAGE_EDITOR"],
+		[PROPERTY_USAGE_INTERNAL, "PROPERTY_USAGE_INTERNAL"],
+		[PROPERTY_USAGE_CHECKABLE, "PROPERTY_USAGE_CHECKABLE"],
+		[PROPERTY_USAGE_CHECKED, "PROPERTY_USAGE_CHECKED"],
+		[PROPERTY_USAGE_GROUP, "PROPERTY_USAGE_GROUP"],
+		[PROPERTY_USAGE_CATEGORY, "PROPERTY_USAGE_CATEGORY"],
+		[PROPERTY_USAGE_SUBGROUP, "PROPERTY_USAGE_SUBGROUP"],
+		[PROPERTY_USAGE_CLASS_IS_BITFIELD, "PROPERTY_USAGE_CLASS_IS_BITFIELD"],
+		[PROPERTY_USAGE_NO_INSTANCE_STATE, "PROPERTY_USAGE_NO_INSTANCE_STATE"],
+		[PROPERTY_USAGE_RESTART_IF_CHANGED, "PROPERTY_USAGE_RESTART_IF_CHANGED"],
+		[PROPERTY_USAGE_SCRIPT_VARIABLE, "PROPERTY_USAGE_SCRIPT_VARIABLE"],
+		[PROPERTY_USAGE_STORE_IF_NULL, "PROPERTY_USAGE_STORE_IF_NULL"],
+		[PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED, "PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED"],
+		[PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE, "PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE"],
+		[PROPERTY_USAGE_CLASS_IS_ENUM, "PROPERTY_USAGE_CLASS_IS_ENUM"],
+		[PROPERTY_USAGE_NIL_IS_VARIANT, "PROPERTY_USAGE_NIL_IS_VARIANT"],
+		[PROPERTY_USAGE_ARRAY, "PROPERTY_USAGE_ARRAY"],
+		[PROPERTY_USAGE_ALWAYS_DUPLICATE, "PROPERTY_USAGE_ALWAYS_DUPLICATE"],
+		[PROPERTY_USAGE_NEVER_DUPLICATE, "PROPERTY_USAGE_NEVER_DUPLICATE"],
+		[PROPERTY_USAGE_HIGH_END_GFX, "PROPERTY_USAGE_HIGH_END_GFX"],
+		[PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT, "PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT"],
+		[PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT, "PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT"],
+		[PROPERTY_USAGE_KEYING_INCREMENTS, "PROPERTY_USAGE_KEYING_INCREMENTS"],
+		[PROPERTY_USAGE_DEFERRED_SET_RESOURCE, "PROPERTY_USAGE_DEFERRED_SET_RESOURCE"],
+		[PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT, "PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT"],
+		[PROPERTY_USAGE_EDITOR_BASIC_SETTING, "PROPERTY_USAGE_EDITOR_BASIC_SETTING"],
+		[PROPERTY_USAGE_READ_ONLY, "PROPERTY_USAGE_READ_ONLY"],
+		[PROPERTY_USAGE_SECRET, "PROPERTY_USAGE_SECRET"],
+	]
+
+	var result: String = ""
+
+	for flag in FLAGS:
+		if usage & flag[0]:
+			result += flag[1] + "|"
+			usage &= ~flag[0]
+
+	if usage != PROPERTY_USAGE_NONE:
+		push_error("Argument `usage` is invalid. Use `PROPERTY_USAGE_*` constants.")
+		return "<invalid usage flags>"
+
+	return result.left(-1)