From c084dbbf0500b0d448ecc15a574df5eecb225509 Mon Sep 17 00:00:00 2001 From: Raul Santos Date: Sat, 6 Aug 2022 21:35:31 +0200 Subject: [PATCH] Various fixes to C# documentation generator - Fix references to global constants and members of `@GlobalScope` - Log errors for missing members or methods - Finds referenced members that are derived (they were not found before since it was only looking in the current type) - Hardcodes special case for `_init` method, in C# replaced with a reference to the constructor - Ignores properties with slashes (since they are not declared in C# and can't be referenced) - Refactor `bbcode_to_xml` method --- modules/mono/editor/bindings_generator.cpp | 374 ++++++++++++--------- modules/mono/editor/bindings_generator.h | 7 + 2 files changed, 218 insertions(+), 163 deletions(-) diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 35860300aa1..4dd1320e4f6 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -271,7 +271,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf String link_target = tag.substr(tag.find(" ") + 1, tag.length()); String link_tag = tag.substr(0, tag.find(" ")); - Vector link_target_parts = link_target.split("."); + const Vector link_target_parts = link_target.split("."); if (link_target_parts.size() <= 0 || link_target_parts.size() > 2) { ERR_PRINT("Invalid reference format: '" + tag + "'."); @@ -299,173 +299,16 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf } if (link_tag == "method") { - if (!target_itype || !target_itype->is_object_type) { - if (OS::get_singleton()->is_stdout_verbose()) { - if (target_itype) { - OS::get_singleton()->print("Cannot resolve method reference for non-Godot.Object type in documentation: %s\n", link_target.utf8().get_data()); - } else { - OS::get_singleton()->print("Cannot resolve type from method reference in documentation: %s\n", link_target.utf8().get_data()); - } - } - - // TODO Map what we can - xml_output.append(""); - xml_output.append(link_target); - xml_output.append(""); - } else { - const MethodInterface *target_imethod = target_itype->find_method_by_name(target_cname); - - if (target_imethod) { - xml_output.append("proxy_name); - xml_output.append("."); - xml_output.append(target_imethod->proxy_name); - xml_output.append("\"/>"); - } - } + _append_xml_method(xml_output, target_itype, target_cname, link_target, link_target_parts); } else if (link_tag == "member") { - if (!target_itype || !target_itype->is_object_type) { - if (OS::get_singleton()->is_stdout_verbose()) { - if (target_itype) { - OS::get_singleton()->print("Cannot resolve member reference for non-Godot.Object type in documentation: %s\n", link_target.utf8().get_data()); - } else { - OS::get_singleton()->print("Cannot resolve type from member reference in documentation: %s\n", link_target.utf8().get_data()); - } - } - - // TODO Map what we can - xml_output.append(""); - xml_output.append(link_target); - xml_output.append(""); - } else { - const PropertyInterface *target_iprop = target_itype->find_property_by_name(target_cname); - - if (target_iprop) { - xml_output.append("proxy_name); - xml_output.append("."); - xml_output.append(target_iprop->proxy_name); - xml_output.append("\"/>"); - } - } + _append_xml_member(xml_output, target_itype, target_cname, link_target, link_target_parts); } else if (link_tag == "signal") { // We do not declare signals in any way in C#, so there is nothing to reference - xml_output.append(""); - xml_output.append(link_target); - xml_output.append(""); + _append_xml_undeclared(xml_output, link_target); } else if (link_tag == "enum") { - StringName search_cname = !target_itype ? target_cname : StringName(target_itype->name + "." + (String)target_cname); - - const Map::Element *enum_match = enum_types.find(search_cname); - - if (!enum_match && search_cname != target_cname) { - enum_match = enum_types.find(target_cname); - } - - if (enum_match) { - const TypeInterface &target_enum_itype = enum_match->value(); - - xml_output.append(""); - } else { - ERR_PRINT("Cannot resolve enum reference in documentation: '" + link_target + "'."); - - xml_output.append(""); - xml_output.append(link_target); - xml_output.append(""); - } + _append_xml_enum(xml_output, target_itype, target_cname, link_target, link_target_parts); } else if (link_tag == "constant") { - if (!target_itype || !target_itype->is_object_type) { - if (OS::get_singleton()->is_stdout_verbose()) { - if (target_itype) { - OS::get_singleton()->print("Cannot resolve constant reference for non-Godot.Object type in documentation: %s\n", link_target.utf8().get_data()); - } else { - OS::get_singleton()->print("Cannot resolve type from constant reference in documentation: %s\n", link_target.utf8().get_data()); - } - } - - // TODO Map what we can - xml_output.append(""); - xml_output.append(link_target); - xml_output.append(""); - } else if (!target_itype && target_cname == name_cache.type_at_GlobalScope) { - String target_name = (String)target_cname; - - // Try to find as a global constant - const ConstantInterface *target_iconst = find_constant_by_name(target_name, global_constants); - - if (target_iconst) { - // Found global constant - xml_output.append("proxy_name); - xml_output.append("\"/>"); - } else { - // Try to find as global enum constant - const EnumInterface *target_ienum = NULL; - - for (const List::Element *E = global_enums.front(); E; E = E->next()) { - target_ienum = &E->get(); - target_iconst = find_constant_by_name(target_name, target_ienum->constants); - if (target_iconst) - break; - } - - if (target_iconst) { - xml_output.append("cname); - xml_output.append("."); - xml_output.append(target_iconst->proxy_name); - xml_output.append("\"/>"); - } else { - ERR_PRINT("Cannot resolve global constant reference in documentation: '" + link_target + "'."); - - xml_output.append(""); - xml_output.append(link_target); - xml_output.append(""); - } - } - } else { - String target_name = (String)target_cname; - - // Try to find the constant in the current class - const ConstantInterface *target_iconst = find_constant_by_name(target_name, target_itype->constants); - - if (target_iconst) { - // Found constant in current class - xml_output.append("proxy_name); - xml_output.append("."); - xml_output.append(target_iconst->proxy_name); - xml_output.append("\"/>"); - } else { - // Try to find as enum constant in the current class - const EnumInterface *target_ienum = NULL; - - for (const List::Element *E = target_itype->enums.front(); E; E = E->next()) { - target_ienum = &E->get(); - target_iconst = find_constant_by_name(target_name, target_ienum->constants); - if (target_iconst) - break; - } - - if (target_iconst) { - xml_output.append("proxy_name); - xml_output.append("."); - xml_output.append(target_ienum->cname); - xml_output.append("."); - xml_output.append(target_iconst->proxy_name); - xml_output.append("\"/>"); - } else { - ERR_PRINT("Cannot resolve constant reference in documentation: '" + link_target + "'."); - - xml_output.append(""); - xml_output.append(link_target); - xml_output.append(""); - } - } - } + _append_xml_constant(xml_output, target_itype, target_cname, link_target, link_target_parts); } pos = brk_end + 1; @@ -623,6 +466,211 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf return xml_output.as_string(); } +void BindingsGenerator::_append_xml_method(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector &p_link_target_parts) { + if (p_link_target_parts[0] == name_cache.type_at_GlobalScope) { + if (OS::get_singleton()->is_stdout_verbose()) { + OS::get_singleton()->print("Cannot resolve @GlobalScope method reference in documentation: %s\n", p_link_target.utf8().get_data()); + } + + // TODO Map what we can + _append_xml_undeclared(p_xml_output, p_link_target); + } else if (!p_target_itype || !p_target_itype->is_object_type) { + if (OS::get_singleton()->is_stdout_verbose()) { + if (p_target_itype) { + OS::get_singleton()->print("Cannot resolve method reference for non-Godot.Object type in documentation: %s\n", p_link_target.utf8().get_data()); + } else { + OS::get_singleton()->print("Cannot resolve type from method reference in documentation: %s\n", p_link_target.utf8().get_data()); + } + } + + // TODO Map what we can + _append_xml_undeclared(p_xml_output, p_link_target); + } else { + if (p_target_cname == "_init") { + // The _init method is not declared in C#, reference the constructor instead + p_xml_output.append("proxy_name); + p_xml_output.append("."); + p_xml_output.append(p_target_itype->proxy_name); + p_xml_output.append("()\"/>"); + } else { + const MethodInterface *target_imethod = p_target_itype->find_method_by_name(p_target_cname); + + if (target_imethod) { + p_xml_output.append("proxy_name); + p_xml_output.append("."); + p_xml_output.append(target_imethod->proxy_name); + p_xml_output.append("\"/>"); + } else { + ERR_PRINT("Cannot resolve method reference in documentation: '" + p_link_target + "'."); + _append_xml_undeclared(p_xml_output, p_link_target); + } + } + } +} + +void BindingsGenerator::_append_xml_member(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector &p_link_target_parts) { + if (p_link_target.find("/") >= 0) { + // Properties with '/' (slash) in the name are not declared in C#, so there is nothing to reference. + _append_xml_undeclared(p_xml_output, p_link_target); + } else if (!p_target_itype || !p_target_itype->is_object_type) { + if (OS::get_singleton()->is_stdout_verbose()) { + if (p_target_itype) { + OS::get_singleton()->print("Cannot resolve member reference for non-Godot.Object type in documentation: %s\n", p_link_target.utf8().get_data()); + } else { + OS::get_singleton()->print("Cannot resolve type from member reference in documentation: %s\n", p_link_target.utf8().get_data()); + } + } + + // TODO Map what we can + _append_xml_undeclared(p_xml_output, p_link_target); + } else { + const TypeInterface *current_itype = p_target_itype; + const PropertyInterface *target_iprop = NULL; + + while (target_iprop == NULL && current_itype != NULL) { + target_iprop = current_itype->find_property_by_name(p_target_cname); + if (target_iprop == NULL) { + current_itype = _get_type_or_null(TypeReference(current_itype->base_name)); + } + } + + if (target_iprop) { + p_xml_output.append("proxy_name); + p_xml_output.append("."); + p_xml_output.append(target_iprop->proxy_name); + p_xml_output.append("\"/>"); + } else { + ERR_PRINT("Cannot resolve member reference in documentation: '" + p_link_target + "'."); + _append_xml_undeclared(p_xml_output, p_link_target); + } + } +} + +void BindingsGenerator::_append_xml_enum(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector &p_link_target_parts) { + const StringName search_cname = !p_target_itype ? p_target_cname : StringName(p_target_itype->name + "." + (String)p_target_cname); + + const Map::Element *enum_match = enum_types.find(search_cname); + + if (!enum_match && search_cname != p_target_cname) { + enum_match = enum_types.find(p_target_cname); + } + + if (enum_match) { + const TypeInterface &target_enum_itype = enum_match->value(); + + p_xml_output.append(""); + } else { + ERR_PRINT("Cannot resolve enum reference in documentation: '" + p_link_target + "'."); + _append_xml_undeclared(p_xml_output, p_link_target); + } +} + +void BindingsGenerator::_append_xml_constant(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector &p_link_target_parts) { + if (p_link_target_parts[0] == name_cache.type_at_GlobalScope) { + _append_xml_constant_in_global_scope(p_xml_output, p_target_cname, p_link_target); + } else if (!p_target_itype || !p_target_itype->is_object_type) { + // Search in @GlobalScope as a last resort if no class was specified + if (p_link_target_parts.size() == 1) { + _append_xml_constant_in_global_scope(p_xml_output, p_target_cname, p_link_target); + return; + } + + if (OS::get_singleton()->is_stdout_verbose()) { + if (p_target_itype) { + OS::get_singleton()->print("Cannot resolve constant reference for non-Godot.Object type in documentation: %s\n", p_link_target.utf8().get_data()); + } else { + OS::get_singleton()->print("Cannot resolve type from constant reference in documentation: %s\n", p_link_target.utf8().get_data()); + } + } + + // TODO Map what we can + _append_xml_undeclared(p_xml_output, p_link_target); + } else { + // Try to find the constant in the current class + const ConstantInterface *target_iconst = find_constant_by_name(p_target_cname, p_target_itype->constants); + + if (target_iconst) { + // Found constant in current class + p_xml_output.append("proxy_name); + p_xml_output.append("."); + p_xml_output.append(target_iconst->proxy_name); + p_xml_output.append("\"/>"); + } else { + // Try to find as enum constant in the current class + const EnumInterface *target_ienum = NULL; + + for (const List::Element *E = p_target_itype->enums.front(); E; E = E->next()) { + target_ienum = &E->get(); + target_iconst = find_constant_by_name(p_target_cname, target_ienum->constants); + if (target_iconst) { + break; + } + } + + if (target_iconst) { + p_xml_output.append("proxy_name); + p_xml_output.append("."); + p_xml_output.append(target_ienum->cname); + p_xml_output.append("."); + p_xml_output.append(target_iconst->proxy_name); + p_xml_output.append("\"/>"); + } else if (p_link_target_parts.size() == 1) { + // Also search in @GlobalScope as a last resort if no class was specified + _append_xml_constant_in_global_scope(p_xml_output, p_target_cname, p_link_target); + } else { + ERR_PRINT("Cannot resolve constant reference in documentation: '" + p_link_target + "'."); + _append_xml_undeclared(p_xml_output, p_link_target); + } + } + } +} + +void BindingsGenerator::_append_xml_constant_in_global_scope(StringBuilder &p_xml_output, const String &p_target_cname, const String &p_link_target) { + // Try to find as a global constant + const ConstantInterface *target_iconst = find_constant_by_name(p_target_cname, global_constants); + + if (target_iconst) { + // Found global constant + p_xml_output.append("proxy_name); + p_xml_output.append("\"/>"); + } else { + // Try to find as global enum constant + const EnumInterface *target_ienum = NULL; + + for (const List::Element *E = global_enums.front(); E; E = E->next()) { + target_ienum = &E->get(); + target_iconst = find_constant_by_name(p_target_cname, target_ienum->constants); + if (target_iconst) { + break; + } + } + + if (target_iconst) { + p_xml_output.append("cname); + p_xml_output.append("."); + p_xml_output.append(target_iconst->proxy_name); + p_xml_output.append("\"/>"); + } else { + ERR_PRINT("Cannot resolve global constant reference in documentation: '" + p_link_target + "'."); + _append_xml_undeclared(p_xml_output, p_link_target); + } + } +} + +void BindingsGenerator::_append_xml_undeclared(StringBuilder &p_xml_output, const String &p_link_target) { + p_xml_output.append(""); + p_xml_output.append(p_link_target); + p_xml_output.append(""); +} int BindingsGenerator::_determine_enum_prefix(const EnumInterface &p_ienum) { CRASH_COND(p_ienum.constants.empty()); diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index 228b01a810c..73c4330ac9f 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -602,6 +602,13 @@ class BindingsGenerator { String bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype); + void _append_xml_method(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector &p_link_target_parts); + void _append_xml_member(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector &p_link_target_parts); + void _append_xml_enum(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector &p_link_target_parts); + void _append_xml_constant(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector &p_link_target_parts); + void _append_xml_constant_in_global_scope(StringBuilder &p_xml_output, const String &p_target_cname, const String &p_link_target); + void _append_xml_undeclared(StringBuilder &p_xml_output, const String &p_link_target); + int _determine_enum_prefix(const EnumInterface &p_ienum); void _apply_prefix_to_enum_constants(EnumInterface &p_ienum, int p_prefix_length);