diff options
author | RĂ©mi Verschelde <remi@verschelde.fr> | 2022-08-08 19:15:07 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-08 19:15:07 +0200 |
commit | bc3ab0aaa86e88d12300c3939b23bef0def02105 (patch) | |
tree | 7515b1d4b684813e0d133d857c1a3ffb931d738f /editor | |
parent | 68cffc593130cae0ca8295168c51d2656162d833 (diff) | |
parent | 1362bc22bd636190dfaf6876fbec86e2cd7a34b7 (diff) |
Merge pull request #64004 from YuriSizov/doctool-fail-on-unnamed-args
Add checks and tests for empty/unnamed arguments
Diffstat (limited to 'editor')
-rw-r--r-- | editor/doc_tools.cpp | 509 |
1 files changed, 259 insertions, 250 deletions
diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp index a819458417..46a2a0719a 100644 --- a/editor/doc_tools.cpp +++ b/editor/doc_tools.cpp @@ -337,311 +337,318 @@ static Variant get_documentation_default_value(const StringName &p_class_name, c } void DocTools::generate(bool p_basic_types) { - List<StringName> classes; - ClassDB::get_class_list(&classes); - classes.sort_custom<StringName::AlphCompare>(); - // Move ProjectSettings, so that other classes can register properties there. - classes.move_to_back(classes.find("ProjectSettings")); - - bool skip_setter_getter_methods = true; - - while (classes.size()) { - HashSet<StringName> setters_getters; - - String name = classes.front()->get(); - if (!ClassDB::is_class_exposed(name)) { - print_verbose(vformat("Class '%s' is not exposed, skipping.", name)); - classes.pop_front(); - continue; - } - - String cname = name; - - class_list[cname] = DocData::ClassDoc(); - DocData::ClassDoc &c = class_list[cname]; - c.name = cname; - c.inherits = ClassDB::get_parent_class(name); + // Add ClassDB-exposed classes. + { + List<StringName> classes; + ClassDB::get_class_list(&classes); + classes.sort_custom<StringName::AlphCompare>(); + // Move ProjectSettings, so that other classes can register properties there. + classes.move_to_back(classes.find("ProjectSettings")); + + bool skip_setter_getter_methods = true; + + // Populate documentation data for each exposed class. + while (classes.size()) { + String name = classes.front()->get(); + if (!ClassDB::is_class_exposed(name)) { + print_verbose(vformat("Class '%s' is not exposed, skipping.", name)); + classes.pop_front(); + continue; + } - List<PropertyInfo> properties; - List<PropertyInfo> own_properties; - - // Special case for editor and project settings, so they can be documented. - if (name == "EditorSettings") { - // We don't create the full blown EditorSettings (+ config file) with `create()`, - // instead we just make a local instance to get default values. - Ref<EditorSettings> edset = memnew(EditorSettings); - edset->get_property_list(&properties); - own_properties = properties; - } else if (name == "ProjectSettings") { - ProjectSettings::get_singleton()->get_property_list(&properties); - own_properties = properties; - } else { - ClassDB::get_property_list(name, &properties); - ClassDB::get_property_list(name, &own_properties, true); - } + String cname = name; + // Property setters and getters do not get exposed as individual methods. + HashSet<StringName> setters_getters; - properties.sort(); - own_properties.sort(); + class_list[cname] = DocData::ClassDoc(); + DocData::ClassDoc &c = class_list[cname]; + c.name = cname; + c.inherits = ClassDB::get_parent_class(name); - List<PropertyInfo>::Element *EO = own_properties.front(); - for (const PropertyInfo &E : properties) { - bool inherited = true; - if (EO && EO->get() == E) { - inherited = false; - EO = EO->next(); - } + List<PropertyInfo> properties; + List<PropertyInfo> own_properties; - if (E.usage & PROPERTY_USAGE_GROUP || E.usage & PROPERTY_USAGE_SUBGROUP || E.usage & PROPERTY_USAGE_CATEGORY || E.usage & PROPERTY_USAGE_INTERNAL || (E.type == Variant::NIL && E.usage & PROPERTY_USAGE_ARRAY)) { - continue; + // Special case for editor and project settings, so they can be documented. + if (name == "EditorSettings") { + // We don't create the full blown EditorSettings (+ config file) with `create()`, + // instead we just make a local instance to get default values. + Ref<EditorSettings> edset = memnew(EditorSettings); + edset->get_property_list(&properties); + own_properties = properties; + } else if (name == "ProjectSettings") { + ProjectSettings::get_singleton()->get_property_list(&properties); + own_properties = properties; + } else { + ClassDB::get_property_list(name, &properties); + ClassDB::get_property_list(name, &own_properties, true); } - DocData::PropertyDoc prop; - prop.name = E.name; - prop.overridden = inherited; + properties.sort(); + own_properties.sort(); - if (inherited) { - String parent = ClassDB::get_parent_class(c.name); - while (!ClassDB::has_property(parent, prop.name, true)) { - parent = ClassDB::get_parent_class(parent); + List<PropertyInfo>::Element *EO = own_properties.front(); + for (const PropertyInfo &E : properties) { + bool inherited = true; + if (EO && EO->get() == E) { + inherited = false; + EO = EO->next(); } - prop.overrides = parent; - } - - bool default_value_valid = false; - Variant default_value; - if (name == "EditorSettings") { - if (E.name == "resource_local_to_scene" || E.name == "resource_name" || E.name == "resource_path" || E.name == "script") { - // Don't include spurious properties in the generated EditorSettings class reference. + if (E.usage & PROPERTY_USAGE_GROUP || E.usage & PROPERTY_USAGE_SUBGROUP || E.usage & PROPERTY_USAGE_CATEGORY || E.usage & PROPERTY_USAGE_INTERNAL || (E.type == Variant::NIL && E.usage & PROPERTY_USAGE_ARRAY)) { continue; } - } - if (name == "ProjectSettings") { - // Special case for project settings, so that settings are not taken from the current project's settings - if (E.name == "script" || !ProjectSettings::get_singleton()->is_builtin_setting(E.name)) { - continue; - } - if (E.usage & PROPERTY_USAGE_EDITOR) { - if (!ProjectSettings::get_singleton()->get_ignore_value_in_docs(E.name)) { - default_value = ProjectSettings::get_singleton()->property_get_revert(E.name); - default_value_valid = true; + DocData::PropertyDoc prop; + prop.name = E.name; + prop.overridden = inherited; + + if (inherited) { + String parent = ClassDB::get_parent_class(c.name); + while (!ClassDB::has_property(parent, prop.name, true)) { + parent = ClassDB::get_parent_class(parent); } + prop.overrides = parent; } - } else { - default_value = get_documentation_default_value(name, E.name, default_value_valid); - if (inherited) { - bool base_default_value_valid = false; - Variant base_default_value = get_documentation_default_value(ClassDB::get_parent_class(name), E.name, base_default_value_valid); - if (!default_value_valid || !base_default_value_valid || default_value == base_default_value) { + + bool default_value_valid = false; + Variant default_value; + + if (name == "EditorSettings") { + if (E.name == "resource_local_to_scene" || E.name == "resource_name" || E.name == "resource_path" || E.name == "script") { + // Don't include spurious properties in the generated EditorSettings class reference. continue; } } - } - if (default_value_valid && default_value.get_type() != Variant::OBJECT) { - prop.default_value = default_value.get_construct_string().replace("\n", " "); - } - - StringName setter = ClassDB::get_property_setter(name, E.name); - StringName getter = ClassDB::get_property_getter(name, E.name); - - prop.setter = setter; - prop.getter = getter; - - bool found_type = false; - if (getter != StringName()) { - MethodBind *mb = ClassDB::get_method(name, getter); - if (mb) { - PropertyInfo retinfo = mb->get_return_info(); - - found_type = true; - if (retinfo.type == Variant::INT && retinfo.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) { - prop.enumeration = retinfo.class_name; - prop.type = "int"; - } else if (retinfo.class_name != StringName()) { - prop.type = retinfo.class_name; - } else if (retinfo.type == Variant::ARRAY && retinfo.hint == PROPERTY_HINT_ARRAY_TYPE) { - prop.type = retinfo.hint_string + "[]"; - } else if (retinfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { - prop.type = retinfo.hint_string; - } else if (retinfo.type == Variant::NIL && retinfo.usage & PROPERTY_USAGE_NIL_IS_VARIANT) { - prop.type = "Variant"; - } else if (retinfo.type == Variant::NIL) { - prop.type = "void"; - } else { - prop.type = Variant::get_type_name(retinfo.type); + if (name == "ProjectSettings") { + // Special case for project settings, so that settings are not taken from the current project's settings + if (E.name == "script" || !ProjectSettings::get_singleton()->is_builtin_setting(E.name)) { + continue; + } + if (E.usage & PROPERTY_USAGE_EDITOR) { + if (!ProjectSettings::get_singleton()->get_ignore_value_in_docs(E.name)) { + default_value = ProjectSettings::get_singleton()->property_get_revert(E.name); + default_value_valid = true; + } + } + } else { + default_value = get_documentation_default_value(name, E.name, default_value_valid); + if (inherited) { + bool base_default_value_valid = false; + Variant base_default_value = get_documentation_default_value(ClassDB::get_parent_class(name), E.name, base_default_value_valid); + if (!default_value_valid || !base_default_value_valid || default_value == base_default_value) { + continue; + } } } - setters_getters.insert(getter); - } + if (default_value_valid && default_value.get_type() != Variant::OBJECT) { + prop.default_value = default_value.get_construct_string().replace("\n", " "); + } - if (setter != StringName()) { - setters_getters.insert(setter); - } + StringName setter = ClassDB::get_property_setter(name, E.name); + StringName getter = ClassDB::get_property_getter(name, E.name); + + prop.setter = setter; + prop.getter = getter; + + bool found_type = false; + if (getter != StringName()) { + MethodBind *mb = ClassDB::get_method(name, getter); + if (mb) { + PropertyInfo retinfo = mb->get_return_info(); + + found_type = true; + if (retinfo.type == Variant::INT && retinfo.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) { + prop.enumeration = retinfo.class_name; + prop.type = "int"; + } else if (retinfo.class_name != StringName()) { + prop.type = retinfo.class_name; + } else if (retinfo.type == Variant::ARRAY && retinfo.hint == PROPERTY_HINT_ARRAY_TYPE) { + prop.type = retinfo.hint_string + "[]"; + } else if (retinfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { + prop.type = retinfo.hint_string; + } else if (retinfo.type == Variant::NIL && retinfo.usage & PROPERTY_USAGE_NIL_IS_VARIANT) { + prop.type = "Variant"; + } else if (retinfo.type == Variant::NIL) { + prop.type = "void"; + } else { + prop.type = Variant::get_type_name(retinfo.type); + } + } - if (!found_type) { - if (E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_RESOURCE_TYPE) { - prop.type = E.hint_string; - } else { - prop.type = Variant::get_type_name(E.type); + setters_getters.insert(getter); } - } - c.properties.push_back(prop); - } + if (setter != StringName()) { + setters_getters.insert(setter); + } - List<MethodInfo> method_list; - ClassDB::get_method_list(name, &method_list, true); - method_list.sort(); + if (!found_type) { + if (E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_RESOURCE_TYPE) { + prop.type = E.hint_string; + } else { + prop.type = Variant::get_type_name(E.type); + } + } - for (const MethodInfo &E : method_list) { - if (E.name.is_empty() || (E.name[0] == '_' && !(E.flags & METHOD_FLAG_VIRTUAL))) { - continue; //hidden, don't count + c.properties.push_back(prop); } - if (skip_setter_getter_methods && setters_getters.has(E.name)) { - // Don't skip parametric setters and getters, i.e. method which require - // one or more parameters to define what property should be set or retrieved. - // E.g. CPUParticles3D::set_param(Parameter param, float value). - if (E.arguments.size() == 0 /* getter */ || (E.arguments.size() == 1 && E.return_val.type == Variant::NIL /* setter */)) { - continue; - } - } + List<MethodInfo> method_list; + ClassDB::get_method_list(name, &method_list, true); + method_list.sort(); - DocData::MethodDoc method; - DocData::method_doc_from_methodinfo(method, E, ""); + for (const MethodInfo &E : method_list) { + if (E.name.is_empty() || (E.name[0] == '_' && !(E.flags & METHOD_FLAG_VIRTUAL))) { + continue; //hidden, don't count + } - Vector<Error> errs = ClassDB::get_method_error_return_values(name, E.name); - if (errs.size()) { - if (!errs.has(OK)) { - errs.insert(0, OK); + if (skip_setter_getter_methods && setters_getters.has(E.name)) { + // Don't skip parametric setters and getters, i.e. method which require + // one or more parameters to define what property should be set or retrieved. + // E.g. CPUParticles3D::set_param(Parameter param, float value). + if (E.arguments.size() == 0 /* getter */ || (E.arguments.size() == 1 && E.return_val.type == Variant::NIL /* setter */)) { + continue; + } } - for (int i = 0; i < errs.size(); i++) { - if (!method.errors_returned.has(errs[i])) { - method.errors_returned.push_back(errs[i]); + + DocData::MethodDoc method; + DocData::method_doc_from_methodinfo(method, E, ""); + + Vector<Error> errs = ClassDB::get_method_error_return_values(name, E.name); + if (errs.size()) { + if (!errs.has(OK)) { + errs.insert(0, OK); + } + for (int i = 0; i < errs.size(); i++) { + if (!method.errors_returned.has(errs[i])) { + method.errors_returned.push_back(errs[i]); + } } } + + c.methods.push_back(method); } - c.methods.push_back(method); - } + List<MethodInfo> signal_list; + ClassDB::get_signal_list(name, &signal_list, true); - List<MethodInfo> signal_list; - ClassDB::get_signal_list(name, &signal_list, true); + if (signal_list.size()) { + for (List<MethodInfo>::Element *EV = signal_list.front(); EV; EV = EV->next()) { + DocData::MethodDoc signal; + signal.name = EV->get().name; + for (int i = 0; i < EV->get().arguments.size(); i++) { + const PropertyInfo &arginfo = EV->get().arguments[i]; + DocData::ArgumentDoc argument; + DocData::argument_doc_from_arginfo(argument, arginfo); - if (signal_list.size()) { - for (List<MethodInfo>::Element *EV = signal_list.front(); EV; EV = EV->next()) { - DocData::MethodDoc signal; - signal.name = EV->get().name; - for (int i = 0; i < EV->get().arguments.size(); i++) { - const PropertyInfo &arginfo = EV->get().arguments[i]; - DocData::ArgumentDoc argument; - DocData::argument_doc_from_arginfo(argument, arginfo); + signal.arguments.push_back(argument); + } - signal.arguments.push_back(argument); + c.signals.push_back(signal); } - - c.signals.push_back(signal); } - } - List<String> constant_list; - ClassDB::get_integer_constant_list(name, &constant_list, true); + List<String> constant_list; + ClassDB::get_integer_constant_list(name, &constant_list, true); + + for (const String &E : constant_list) { + DocData::ConstantDoc constant; + constant.name = E; + constant.value = itos(ClassDB::get_integer_constant(name, E)); + constant.is_value_valid = true; + constant.enumeration = ClassDB::get_integer_constant_enum(name, E); + constant.is_bitfield = ClassDB::is_enum_bitfield(name, constant.enumeration); + c.constants.push_back(constant); + } - for (const String &E : constant_list) { - DocData::ConstantDoc constant; - constant.name = E; - constant.value = itos(ClassDB::get_integer_constant(name, E)); - constant.is_value_valid = true; - constant.enumeration = ClassDB::get_integer_constant_enum(name, E); - constant.is_bitfield = ClassDB::is_enum_bitfield(name, constant.enumeration); - c.constants.push_back(constant); - } + // Theme items. + { + List<StringName> l; + + Theme::get_default()->get_color_list(cname, &l); + for (const StringName &E : l) { + DocData::ThemeItemDoc tid; + tid.name = E; + tid.type = "Color"; + tid.data_type = "color"; + tid.default_value = Variant(Theme::get_default()->get_color(E, cname)).get_construct_string().replace("\n", " "); + c.theme_properties.push_back(tid); + } - // Theme items. - { - List<StringName> l; - - Theme::get_default()->get_color_list(cname, &l); - for (const StringName &E : l) { - DocData::ThemeItemDoc tid; - tid.name = E; - tid.type = "Color"; - tid.data_type = "color"; - tid.default_value = Variant(Theme::get_default()->get_color(E, cname)).get_construct_string().replace("\n", " "); - c.theme_properties.push_back(tid); - } + l.clear(); + Theme::get_default()->get_constant_list(cname, &l); + for (const StringName &E : l) { + DocData::ThemeItemDoc tid; + tid.name = E; + tid.type = "int"; + tid.data_type = "constant"; + tid.default_value = itos(Theme::get_default()->get_constant(E, cname)); + c.theme_properties.push_back(tid); + } - l.clear(); - Theme::get_default()->get_constant_list(cname, &l); - for (const StringName &E : l) { - DocData::ThemeItemDoc tid; - tid.name = E; - tid.type = "int"; - tid.data_type = "constant"; - tid.default_value = itos(Theme::get_default()->get_constant(E, cname)); - c.theme_properties.push_back(tid); - } + l.clear(); + Theme::get_default()->get_font_list(cname, &l); + for (const StringName &E : l) { + DocData::ThemeItemDoc tid; + tid.name = E; + tid.type = "Font"; + tid.data_type = "font"; + c.theme_properties.push_back(tid); + } - l.clear(); - Theme::get_default()->get_font_list(cname, &l); - for (const StringName &E : l) { - DocData::ThemeItemDoc tid; - tid.name = E; - tid.type = "Font"; - tid.data_type = "font"; - c.theme_properties.push_back(tid); - } + l.clear(); + Theme::get_default()->get_font_size_list(cname, &l); + for (const StringName &E : l) { + DocData::ThemeItemDoc tid; + tid.name = E; + tid.type = "int"; + tid.data_type = "font_size"; + c.theme_properties.push_back(tid); + } - l.clear(); - Theme::get_default()->get_font_size_list(cname, &l); - for (const StringName &E : l) { - DocData::ThemeItemDoc tid; - tid.name = E; - tid.type = "int"; - tid.data_type = "font_size"; - c.theme_properties.push_back(tid); - } + l.clear(); + Theme::get_default()->get_icon_list(cname, &l); + for (const StringName &E : l) { + DocData::ThemeItemDoc tid; + tid.name = E; + tid.type = "Texture2D"; + tid.data_type = "icon"; + c.theme_properties.push_back(tid); + } - l.clear(); - Theme::get_default()->get_icon_list(cname, &l); - for (const StringName &E : l) { - DocData::ThemeItemDoc tid; - tid.name = E; - tid.type = "Texture2D"; - tid.data_type = "icon"; - c.theme_properties.push_back(tid); - } + l.clear(); + Theme::get_default()->get_stylebox_list(cname, &l); + for (const StringName &E : l) { + DocData::ThemeItemDoc tid; + tid.name = E; + tid.type = "StyleBox"; + tid.data_type = "style"; + c.theme_properties.push_back(tid); + } - l.clear(); - Theme::get_default()->get_stylebox_list(cname, &l); - for (const StringName &E : l) { - DocData::ThemeItemDoc tid; - tid.name = E; - tid.type = "StyleBox"; - tid.data_type = "style"; - c.theme_properties.push_back(tid); + c.theme_properties.sort(); } - c.theme_properties.sort(); + classes.pop_front(); } - - classes.pop_front(); } + // Add a dummy Variant entry. { - // So we can document the concept of Variant even if it's not a usable class per se. + // This allows us to document the concept of Variant even though + // it's not a ClassDB-exposed class. class_list["Variant"] = DocData::ClassDoc(); class_list["Variant"].name = "Variant"; } + // If we don't want to populate basic types, break here. if (!p_basic_types) { return; } - // Add Variant types. + // Add Variant data types. for (int i = 0; i < Variant::VARIANT_MAX; i++) { if (i == Variant::NIL) { continue; // Not exposed outside of 'null', should not be in class list. @@ -809,14 +816,14 @@ void DocTools::generate(bool p_basic_types) { } } - //built in constants and functions - + // Add global API (servers, engine singletons, global constants) and Variant utility functions. { String cname = "@GlobalScope"; class_list[cname] = DocData::ClassDoc(); DocData::ClassDoc &c = class_list[cname]; c.name = cname; + // Global constants. for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) { DocData::ConstantDoc cd; cd.name = CoreConstants::get_global_constant_name(i); @@ -830,10 +837,11 @@ void DocTools::generate(bool p_basic_types) { c.constants.push_back(cd); } + // Servers/engine singletons. List<Engine::Singleton> singletons; Engine::get_singleton()->get_singletons(&singletons); - //servers (this is kind of hackish) + // FIXME: this is kind of hackish... for (const Engine::Singleton &s : singletons) { DocData::PropertyDoc pd; if (!s.ptr) { @@ -847,13 +855,14 @@ void DocTools::generate(bool p_basic_types) { c.properties.push_back(pd); } + // Variant utility functions. List<StringName> utility_functions; Variant::get_utility_function_list(&utility_functions); utility_functions.sort_custom<StringName::AlphCompare>(); for (const StringName &E : utility_functions) { DocData::MethodDoc md; md.name = E; - //return + // Utility function's return type. if (Variant::has_utility_function_return_value(E)) { PropertyInfo pi; pi.type = Variant::get_utility_function_return_type(E); @@ -865,6 +874,7 @@ void DocTools::generate(bool p_basic_types) { md.return_type = ad.type; } + // Utility function's arguments. if (Variant::is_utility_function_vararg(E)) { md.qualifiers = "vararg"; } else { @@ -885,11 +895,10 @@ void DocTools::generate(bool p_basic_types) { } } - // Built-in script reference. - // We only add a doc entry for languages which actually define any built-in - // methods or constants. - + // Add scripting language built-ins. { + // We only add a doc entry for languages which actually define any built-in + // methods, constants, or annotations. for (int i = 0; i < ScriptServer::get_language_count(); i++) { ScriptLanguage *lang = ScriptServer::get_language(i); String cname = "@" + lang->get_name(); |