From 366ec895b1b73e3d509fbc591a6289c0d03774aa Mon Sep 17 00:00:00 2001 From: "ocean (they/them)" Date: Sat, 3 Dec 2022 22:02:03 -0500 Subject: [PATCH] Assorted enum and native type fixes --- modules/gdscript/gdscript_analyzer.cpp | 207 ++++++++++++------ modules/gdscript/gdscript_analyzer.h | 2 +- modules/gdscript/gdscript_compiler.cpp | 2 +- modules/gdscript/gdscript_editor.cpp | 19 +- modules/gdscript/gdscript_parser.cpp | 25 ++- modules/gdscript/gdscript_parser.h | 4 +- .../scripts/analyzer/errors/assign_enum.gd | 3 + .../scripts/analyzer/errors/assign_enum.out | 2 + .../analyzer/errors/assign_named_enum.gd | 3 + .../analyzer/errors/assign_named_enum.out | 2 + .../analyzer/errors/cast_enum_bad_enum.gd | 5 + .../analyzer/errors/cast_enum_bad_enum.out | 2 + .../analyzer/errors/cast_enum_bad_int.gd | 4 + .../analyzer/errors/cast_enum_bad_int.out | 2 + .../analyzer/errors/enum_bad_method.gd | 4 + .../analyzer/errors/enum_bad_method.out | 2 + .../scripts/analyzer/errors/enum_bad_value.gd | 4 + .../analyzer/errors/enum_bad_value.out | 2 + ..._class_var_assign_with_wrong_enum_type.out | 2 +- ...um_class_var_init_with_wrong_enum_type.out | 2 +- .../errors/enum_duplicate_bad_method.gd | 5 + .../errors/enum_duplicate_bad_method.out | 2 + .../enum_function_parameter_wrong_type.gd | 8 + .../enum_function_parameter_wrong_type.out | 2 + .../errors/enum_function_return_wrong_type.gd | 8 + .../enum_function_return_wrong_type.out | 2 + ...l_var_assign_outer_with_wrong_enum_type.gd | 10 + ..._var_assign_outer_with_wrong_enum_type.out | 2 + ..._local_var_assign_with_wrong_enum_type.out | 2 +- ...um_local_var_init_with_wrong_enum_type.out | 2 +- .../analyzer/errors/enum_native_bad_value.gd | 2 + .../analyzer/errors/enum_native_bad_value.out | 2 + .../enum_preload_unnamed_assign_to_named.gd | 6 + .../enum_preload_unnamed_assign_to_named.out | 2 + .../analyzer/errors/enum_shadows_base_enum.gd | 8 + .../errors/enum_shadows_base_enum.out | 2 + .../errors/enum_shadows_outer_enum.gd | 7 + .../errors/enum_shadows_outer_enum.out | 2 + .../errors/enum_unnamed_assign_to_named.gd | 7 + .../errors/enum_unnamed_assign_to_named.out | 2 + .../analyzer/errors/native_type_errors.gd | 2 + .../analyzer/errors/native_type_errors.out | 2 + .../analyzer/errors/preload_enum_error.gd | 6 + .../analyzer/errors/preload_enum_error.out | 2 + .../errors/property_inline_set_type_error.out | 2 +- .../analyzer/features/enum_access_types.gd | 29 +++ .../analyzer/features/enum_access_types.out | 13 ++ .../features/enum_duplicate_into_dict.gd | 13 ++ .../features/enum_duplicate_into_dict.out | 7 + .../analyzer/features/enum_from_base.gd | 13 ++ ...num_from_parent.out => enum_from_base.out} | 0 ...enum_from_parent.gd => enum_from_outer.gd} | 2 - .../analyzer/features/enum_from_outer.out | 4 + .../features/enum_function_typecheck.gd | 112 ++++++++++ .../features/enum_function_typecheck.out | 55 +++++ .../analyzer/features/enum_named_no_shadow.gd | 16 ++ .../features/enum_named_no_shadow.out | 4 + .../features/enum_native_access_types.gd | 19 ++ .../features/enum_native_access_types.out | 9 + .../features/enum_typecheck_inner_class.gd | 86 ++++++++ .../features/enum_typecheck_inner_class.out | 19 ++ 61 files changed, 699 insertions(+), 97 deletions(-) create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/assign_enum.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/assign_enum.out create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.out create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.out create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.out create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.out create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.out create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.out create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.out create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.out create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_outer_enum.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_outer_enum.out create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.out create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out create mode 100644 modules/gdscript/tests/scripts/analyzer/features/enum_access_types.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/features/enum_access_types.out create mode 100644 modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.out create mode 100644 modules/gdscript/tests/scripts/analyzer/features/enum_from_base.gd rename modules/gdscript/tests/scripts/analyzer/features/{enum_from_parent.out => enum_from_base.out} (100%) rename modules/gdscript/tests/scripts/analyzer/features/{enum_from_parent.gd => enum_from_outer.gd} (96%) create mode 100644 modules/gdscript/tests/scripts/analyzer/features/enum_from_outer.out create mode 100644 modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.out create mode 100644 modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.out create mode 100644 modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.out create mode 100644 modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.out diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 8a07d509a10..0f9c75ed20c 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -42,6 +42,9 @@ #include "gdscript_utility_functions.h" #include "scene/resources/packed_scene.h" +#define UNNAMED_ENUM "" +#define ENUM_SEPARATOR "::" + static MethodInfo info_from_utility_func(const StringName &p_function) { ERR_FAIL_COND_V(!Variant::has_utility_function(p_function), MethodInfo()); @@ -106,13 +109,26 @@ static GDScriptParser::DataType make_native_meta_type(const StringName &p_class_ return type; } -static GDScriptParser::DataType make_native_enum_type(const StringName &p_native_class, const StringName &p_enum_name) { +// In enum types, native_type is used to store the class (native or otherwise) that the enum belongs to. +// This disambiguates between similarly named enums in base classes or outer classes +static GDScriptParser::DataType make_enum_type(const StringName &p_enum_name, const String &p_base_name, const bool p_meta = false) { GDScriptParser::DataType type; type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; type.kind = GDScriptParser::DataType::ENUM; - type.builtin_type = Variant::INT; + type.builtin_type = p_meta ? Variant::DICTIONARY : Variant::INT; + type.enum_type = p_enum_name; type.is_constant = true; - type.is_meta_type = true; + type.is_meta_type = p_meta; + + // 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; + + return type; +} + +static GDScriptParser::DataType make_native_enum_type(const StringName &p_enum_name, const StringName &p_native_class, const bool p_meta = true) { + GDScriptParser::DataType type = make_enum_type(p_enum_name, p_native_class, p_meta); List enum_values; ClassDB::get_enum_constants(p_native_class, p_enum_name, &enum_values); @@ -134,6 +150,19 @@ static GDScriptParser::DataType make_builtin_meta_type(Variant::Type p_type) { return type; } +static StringName enum_get_value_name(const GDScriptParser::DataType p_type, int64_t p_val) { + // Check that an enum has a given value, not key. + // Make sure that implicit conversion to int64_t is sensible before calling! + HashMap::ConstIterator i = p_type.enum_values.begin(); + while (i) { + if (i->value == p_val) { + return i->key; + } + ++i; + } + return StringName(); +} + bool GDScriptAnalyzer::has_member_name_conflict_in_script_class(const StringName &p_member_name, const GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_member) { if (p_class->members_indices.has(p_member_name)) { int index = p_class->members_indices[p_member_name]; @@ -192,6 +221,7 @@ Error GDScriptAnalyzer::check_native_member_name_conflict(const StringName &p_me } Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::ClassNode *p_class_node, const StringName &p_member_name, const GDScriptParser::Node *p_member_node) { + // TODO check outer classes for static members only const GDScriptParser::DataType *current_data_type = &p_class_node->base_type; while (current_data_type && current_data_type->kind == GDScriptParser::DataType::Kind::CLASS) { GDScriptParser::ClassNode *current_class_node = current_data_type->class_type; @@ -591,12 +621,17 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type result = ref->get_parser()->head->get_datatype(); } else if (ClassDB::has_enum(parser->current_class->base_type.native_type, first)) { // Native enum in current class. - result = make_native_enum_type(parser->current_class->base_type.native_type, first); + result = make_native_enum_type(first, parser->current_class->base_type.native_type); } else { // Classes in current scope. List script_classes; + bool found = false; get_class_node_current_scope_classes(parser->current_class, &script_classes); for (GDScriptParser::ClassNode *script_class : script_classes) { + if (found) { + break; + } + if (script_class->identifier && script_class->identifier->name == first) { result = script_class->get_datatype(); break; @@ -608,14 +643,17 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type switch (member.type) { case GDScriptParser::ClassNode::Member::CLASS: result = member.get_datatype(); + found = true; break; case GDScriptParser::ClassNode::Member::ENUM: result = member.get_datatype(); + found = true; break; case GDScriptParser::ClassNode::Member::CONSTANT: if (member.get_datatype().is_meta_type) { result = member.get_datatype(); result.is_meta_type = false; + found = true; break; } else if (Ref