From 9a7d66f93fbcdc5e671e78b9e128594c40c3093f Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Wed, 25 Jan 2023 00:56:33 +0100 Subject: [PATCH] Improved rendering of template methods in class diagrams --- .../plantuml/class_diagram_generator.cc | 4 + src/class_diagram/model/class.cc | 42 +------- src/class_diagram/model/class.h | 12 +-- src/class_diagram/model/class_method.cc | 1 + src/class_diagram/model/class_method.h | 7 +- .../visitor/translation_unit_visitor.cc | 18 +--- .../visitor/translation_unit_visitor.h | 42 +++++--- .../model/template_parameter.cc | 6 +- .../model/template_parameter.h | 6 +- src/common/model/template_trait.cc | 97 +++++++++++++++++++ src/common/model/template_trait.h | 54 +++++++++++ src/cx/util.cc | 7 +- src/cx/util.h | 7 +- src/sequence_diagram/model/participant.cc | 77 --------------- src/sequence_diagram/model/participant.h | 29 +----- .../visitor/translation_unit_visitor.cc | 42 ++++---- .../visitor/translation_unit_visitor.h | 20 ++-- tests/CMakeLists.txt | 1 - tests/t00051/test_case.h | 4 +- tests/t00052/.clang-uml | 12 +++ tests/t00052/t00052.cc | 19 ++++ tests/t00052/test_case.h | 50 ++++++++++ tests/test_cases.cc | 1 + tests/test_cases.yaml | 3 + 24 files changed, 335 insertions(+), 226 deletions(-) rename src/{class_diagram => common}/model/template_parameter.cc (97%) rename src/{class_diagram => common}/model/template_parameter.h (96%) create mode 100644 src/common/model/template_trait.cc create mode 100644 src/common/model/template_trait.h create mode 100644 tests/t00052/.clang-uml create mode 100644 tests/t00052/t00052.cc create mode 100644 tests/t00052/test_case.h diff --git a/src/class_diagram/generators/plantuml/class_diagram_generator.cc b/src/class_diagram/generators/plantuml/class_diagram_generator.cc index f139de4f..5b28bb70 100644 --- a/src/class_diagram/generators/plantuml/class_diagram_generator.cc +++ b/src/class_diagram/generators/plantuml/class_diagram_generator.cc @@ -144,6 +144,10 @@ void generator::generate(const class_ &c, std::ostream &ostr) const ostr << plantuml_common::to_plantuml(m.access()) << m.name(); + if (!m.templates().empty()) { + m.render_template_params(ostr, m_config.using_namespace(), false); + } + ostr << "("; if (m_config.generate_method_arguments() != config::method_arguments::none) { diff --git a/src/class_diagram/model/class.cc b/src/class_diagram/model/class.cc index 96b7a139..1968f85f 100644 --- a/src/class_diagram/model/class.cc +++ b/src/class_diagram/model/class.cc @@ -68,29 +68,12 @@ void class_::add_parent(class_parent &&parent) bases_.emplace_back(std::move(parent)); } -void class_::add_template(template_parameter &&tmplt) -{ - templates_.emplace_back(std::move(tmplt)); -} - const std::vector &class_::members() const { return members_; } const std::vector &class_::methods() const { return methods_; } const std::vector &class_::parents() const { return bases_; } -const std::vector &class_::templates() const -{ - return templates_; -} - -void class_::set_base_template(const std::string &full_name) -{ - base_template_full_name_ = full_name; -} - -std::string class_::base_template() const { return base_template_full_name_; } - bool operator==(const class_ &l, const class_ &r) { return l.id() == r.id(); } std::string class_::full_name_no_ns() const @@ -101,7 +84,7 @@ std::string class_::full_name_no_ns() const ostr << name(); - render_template_params(ostr, false); + render_template_params(ostr, using_namespace(), false); return ostr.str(); } @@ -114,7 +97,8 @@ std::string class_::full_name(bool relative) const std::ostringstream ostr; ostr << name_and_ns(); - render_template_params(ostr, relative); + + render_template_params(ostr, using_namespace(), relative); std::string res; @@ -129,26 +113,6 @@ std::string class_::full_name(bool relative) const return res; } -std::ostringstream &class_::render_template_params( - std::ostringstream &ostr, bool relative) const -{ - using clanguml::common::model::namespace_; - - if (!templates_.empty()) { - std::vector tnames; - std::vector tnames_simplified; - - std::transform(templates_.cbegin(), templates_.cend(), - std::back_inserter(tnames), - [ns = using_namespace(), relative]( - const auto &tmplt) { return tmplt.to_string(ns, relative); }); - - ostr << fmt::format("<{}>", fmt::join(tnames, ",")); - } - - return ostr; -} - bool class_::is_abstract() const { // TODO check if all base abstract methods are overriden diff --git a/src/class_diagram/model/class.h b/src/class_diagram/model/class.h index eb9af5ef..0fcbfa76 100644 --- a/src/class_diagram/model/class.h +++ b/src/class_diagram/model/class.h @@ -23,8 +23,9 @@ #include "common/model/element.h" #include "common/model/enums.h" #include "common/model/stylable_element.h" +#include "common/model/template_parameter.h" +#include "common/model/template_trait.h" #include "common/types.h" -#include "template_parameter.h" #include #include @@ -32,7 +33,8 @@ namespace clanguml::class_diagram::model { class class_ : public common::model::element, - public common::model::stylable_element { + public common::model::stylable_element, + public template_trait { public: class_(const common::model::namespace_ &using_namespace); @@ -55,12 +57,10 @@ public: void add_member(class_member &&member); void add_method(class_method &&method); void add_parent(class_parent &&parent); - void add_template(template_parameter &&tmplt); const std::vector &members() const; const std::vector &methods() const; const std::vector &parents() const; - const std::vector &templates() const; void set_base_template(const std::string &full_name); std::string base_template() const; @@ -85,9 +85,6 @@ public: const class_ &other, const std::string &full_name) const; private: - std::ostringstream &render_template_params( - std::ostringstream &ostr, bool relative) const; - bool is_struct_{false}; bool is_template_{false}; bool is_template_instantiation_{false}; @@ -95,7 +92,6 @@ private: std::vector members_; std::vector methods_; std::vector bases_; - std::vector templates_; std::string base_template_full_name_; std::string full_name_; diff --git a/src/class_diagram/model/class_method.cc b/src/class_diagram/model/class_method.cc index fe84369b..3a3e5868 100644 --- a/src/class_diagram/model/class_method.cc +++ b/src/class_diagram/model/class_method.cc @@ -61,4 +61,5 @@ void class_method::add_parameter(method_parameter &¶meter) { parameters_.emplace_back(std::move(parameter)); } + } // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/model/class_method.h b/src/class_diagram/model/class_method.h index f047863e..d2e2b81e 100644 --- a/src/class_diagram/model/class_method.h +++ b/src/class_diagram/model/class_method.h @@ -18,6 +18,8 @@ #pragma once #include "class_element.h" +#include "common/model/template_parameter.h" +#include "common/model/template_trait.h" #include "method_parameter.h" #include @@ -25,7 +27,9 @@ namespace clanguml::class_diagram::model { -class class_method : public class_element { +using clanguml::common::model::template_trait; + +class class_method : public class_element, public template_trait { public: class_method(common::model::access_t access, const std::string &name, const std::string &type); @@ -52,6 +56,7 @@ public: private: std::vector parameters_; + bool is_pure_virtual_{false}; bool is_virtual_{false}; bool is_const_{false}; diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index c3ecdfcd..a1548d52 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -26,19 +26,6 @@ namespace clanguml::class_diagram::visitor { -using clanguml::class_diagram::model::class_; -using clanguml::class_diagram::model::class_member; -using clanguml::class_diagram::model::class_method; -using clanguml::class_diagram::model::class_parent; -using clanguml::class_diagram::model::diagram; -using clanguml::class_diagram::model::enum_; -using clanguml::class_diagram::model::method_parameter; -using clanguml::class_diagram::model::template_parameter; -using clanguml::common::model::access_t; -using clanguml::common::model::namespace_; -using clanguml::common::model::relationship; -using clanguml::common::model::relationship_t; - translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm, clanguml::class_diagram::model::diagram &diagram, const clanguml::config::class_diagram &config) @@ -513,7 +500,8 @@ void translation_unit_visitor::process_class_declaration( } bool translation_unit_visitor::process_template_parameters( - const clang::ClassTemplateDecl &template_declaration, class_ &c) + const clang::TemplateDecl &template_declaration, + common::model::template_trait &c) { LOG_DBG("Processing class {} template parameters...", common::get_qualified_name(template_declaration)); @@ -966,6 +954,8 @@ void translation_unit_visitor::process_template_method( method.is_defaulted(mf.getTemplatedDecl()->isDefaulted()); method.is_static(mf.getTemplatedDecl()->isStatic()); + process_template_parameters(mf, method); + process_comment(mf, method); if (method.skip()) diff --git a/src/class_diagram/visitor/translation_unit_visitor.h b/src/class_diagram/visitor/translation_unit_visitor.h index 0e7dcfe8..9313087c 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.h +++ b/src/class_diagram/visitor/translation_unit_visitor.h @@ -20,6 +20,7 @@ #include "class_diagram/model/class.h" #include "class_diagram/model/diagram.h" #include "common/model/enums.h" +#include "common/model/template_trait.h" #include "common/visitor/translation_unit_visitor.h" #include "config/config.h" @@ -34,6 +35,20 @@ namespace clanguml::class_diagram::visitor { +using clanguml::class_diagram::model::class_; +using clanguml::class_diagram::model::class_member; +using clanguml::class_diagram::model::class_method; +using clanguml::class_diagram::model::class_parent; +using clanguml::class_diagram::model::diagram; +using clanguml::class_diagram::model::enum_; +using clanguml::class_diagram::model::method_parameter; +using clanguml::common::model::access_t; +using clanguml::common::model::namespace_; +using clanguml::common::model::relationship; +using clanguml::common::model::relationship_t; +using clanguml::common::model::template_parameter; +using clanguml::common::model::template_trait; + using found_relationships_t = std::vector>; @@ -108,12 +123,11 @@ private: clang::ClassTemplateSpecializationDecl *cls); void process_template_specialization_children( - const clang::ClassTemplateSpecializationDecl *cls, - clanguml::class_diagram::model::class_ &c); + const clang::ClassTemplateSpecializationDecl *cls, class_ &c); bool process_template_parameters( - const clang::ClassTemplateDecl &template_declaration, - clanguml::class_diagram::model::class_ &c); + const clang::TemplateDecl &template_declaration, + clanguml::common::model::template_trait &t); void process_template_specialization_argument( const clang::ClassTemplateSpecializationDecl *cls, @@ -171,7 +185,7 @@ private: clanguml::class_diagram::model::class_ &tinst, std::deque> &template_base_params, int arg_index, bool variadic_params, - const clanguml::class_diagram::model::template_parameter &ct) const; + const clanguml::common::model::template_parameter &ct) const; void build_template_instantiation_process_template_arguments( std::optional &parent, @@ -186,15 +200,15 @@ private: const std::string &full_template_specialization_name, const clang::TemplateDecl *template_decl, const clang::TemplateArgument &arg, - model::template_parameter &argument); + common::model::template_parameter &argument); void build_template_instantiation_process_expression_argument( const clang::TemplateArgument &arg, - model::template_parameter &argument) const; + common::model::template_parameter &argument) const; void build_template_instantiation_process_integral_argument( const clang::TemplateArgument &arg, - model::template_parameter &argument) const; + common::model::template_parameter &argument) const; void build_template_instantiation_process_type_argument( std::optional &parent, @@ -202,11 +216,11 @@ private: const clang::TemplateDecl *template_decl, const clang::TemplateArgument &arg, model::class_ &template_instantiation, - model::template_parameter &argument); + common::model::template_parameter &argument); void build_template_instantiation_process_template_argument( const clang::TemplateArgument &arg, - model::template_parameter &argument) const; + common::model::template_parameter &argument) const; void ensure_lambda_type_is_relative(std::string ¶meter_type) const; @@ -220,19 +234,19 @@ private: void process_unexposed_template_specialization_parameters( const std::string &tspec, - clanguml::class_diagram::model::template_parameter &tp, + clanguml::common::model::template_parameter &tp, clanguml::class_diagram::model::class_ &c); bool find_relationships_in_unexposed_template_params( - const clanguml::class_diagram::model::template_parameter &ct, + const clanguml::common::model::template_parameter &ct, found_relationships_t &relationships); void add_incomplete_forward_declarations(); void resolve_local_to_global_ids(); - bool simplify_system_template( - model::template_parameter &ct, const std::string &full_name) const; + bool simplify_system_template(common::model::template_parameter &ct, + const std::string &full_name) const; /// Store the mapping from local clang entity id (obtained using /// getID()) method to clang-uml global id diff --git a/src/class_diagram/model/template_parameter.cc b/src/common/model/template_parameter.cc similarity index 97% rename from src/class_diagram/model/template_parameter.cc rename to src/common/model/template_parameter.cc index efba7a7d..ed276b6c 100644 --- a/src/class_diagram/model/template_parameter.cc +++ b/src/common/model/template_parameter.cc @@ -1,5 +1,5 @@ /** - * src/class_diagram/model/template_parameter.cc + * src/common/model/template_parameter.cc * * Copyright (c) 2021-2023 Bartek Kryza * @@ -22,7 +22,7 @@ #include -namespace clanguml::class_diagram::model { +namespace clanguml::common::model { template_parameter::template_parameter(const std::string &type, const std::string &name, std::string default_value, bool is_variadic) @@ -216,4 +216,4 @@ bool template_parameter::find_nested_relationships( return added_aggregation_relationship; } -} // namespace clanguml::class_diagram::model +} // namespace clanguml::common::model diff --git a/src/class_diagram/model/template_parameter.h b/src/common/model/template_parameter.h similarity index 96% rename from src/class_diagram/model/template_parameter.h rename to src/common/model/template_parameter.h index 5f9392fd..3ca0f173 100644 --- a/src/class_diagram/model/template_parameter.h +++ b/src/common/model/template_parameter.h @@ -1,5 +1,5 @@ /** - * src/class_diagram/model/template_parameter.h + * src/common/model/template_parameter.h * * Copyright (c) 2021-2023 Bartek Kryza * @@ -24,7 +24,7 @@ #include #include -namespace clanguml::class_diagram::model { +namespace clanguml::common::model { /// @brief Represents template parameter or template argument /// @@ -130,4 +130,4 @@ private: std::optional id_; }; -} // namespace clanguml::class_diagram::model +} // namespace clanguml::common::model diff --git a/src/common/model/template_trait.cc b/src/common/model/template_trait.cc new file mode 100644 index 00000000..878c02ce --- /dev/null +++ b/src/common/model/template_trait.cc @@ -0,0 +1,97 @@ +/** + * src/common/model/template_trait.cc + * + * Copyright (c) 2021-2023 Bartek Kryza + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common/model/template_trait.h" + +namespace clanguml::common::model { + +std::ostream &template_trait::render_template_params(std::ostream &ostr, + const common::model::namespace_ &using_namespace, bool relative) const +{ + using clanguml::common::model::namespace_; + + if (!templates_.empty()) { + std::vector tnames; + std::vector tnames_simplified; + + std::transform(templates_.cbegin(), templates_.cend(), + std::back_inserter(tnames), + [ns = using_namespace, relative]( + const auto &tmplt) { return tmplt.to_string(ns, relative); }); + + ostr << fmt::format("<{}>", fmt::join(tnames, ",")); + } + + return ostr; +} + +void template_trait::set_base_template(const std::string &full_name) +{ + base_template_full_name_ = full_name; +} + +std::string template_trait::base_template() const +{ + return base_template_full_name_; +} + +void template_trait::add_template(template_parameter &&tmplt) +{ + templates_.push_back(std::move(tmplt)); +} + +bool template_trait::is_implicit() const { return is_implicit_; } + +void template_trait::set_implicit(bool implicit) { is_implicit_ = implicit; } + +const std::vector &template_trait::templates() const +{ + return templates_; +} + +int template_trait::calculate_template_specialization_match( + const template_trait &other, const std::string & /*full_name*/) const +{ + int res{}; + + // TODO: handle variadic templates + if (templates().size() != other.templates().size()) { + return res; + } + + // Iterate over all template arguments + for (auto i = 0U; i < other.templates().size(); i++) { + const auto &template_arg = templates().at(i); + const auto &other_template_arg = other.templates().at(i); + + if (template_arg == other_template_arg) { + res++; + } + else if (other_template_arg.is_specialization_of(template_arg)) { + continue; + } + else { + res = 0; + break; + } + } + + return res; +} + +} // namespace clanguml::common::model \ No newline at end of file diff --git a/src/common/model/template_trait.h b/src/common/model/template_trait.h new file mode 100644 index 00000000..9adc5315 --- /dev/null +++ b/src/common/model/template_trait.h @@ -0,0 +1,54 @@ +/** + * src/common/model/template_trait.h + * + * Copyright (c) 2021-2023 Bartek Kryza + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "common/model/element.h" +#include "common/model/template_parameter.h" + +#include +#include + +namespace clanguml::common::model { + +class template_trait { +public: + std::ostream &render_template_params(std::ostream &ostr, + const common::model::namespace_ &using_namespace, bool relative) const; + + void set_base_template(const std::string &full_name); + + std::string base_template() const; + + void add_template(template_parameter &&tmplt); + + const std::vector &templates() const; + + int calculate_template_specialization_match( + const template_trait &other, const std::string &full_name) const; + + bool is_implicit() const; + + void set_implicit(bool implicit); + +private: + std::vector templates_; + std::string base_template_full_name_; + bool is_implicit_{false}; +}; + +} // namespace clanguml::common::model diff --git a/src/cx/util.cc b/src/cx/util.cc index 73684113..a7b0b69d 100644 --- a/src/cx/util.cc +++ b/src/cx/util.cc @@ -21,7 +21,6 @@ #include -#include #include namespace clanguml::cx::util { @@ -39,12 +38,12 @@ std::pair split_ns( return {ns, name}; } -std::vector -parse_unexposed_template_params(const std::string ¶ms, +std::vector parse_unexposed_template_params( + const std::string ¶ms, const std::function &ns_resolve, int depth) { - using class_diagram::model::template_parameter; + using common::model::template_parameter; std::vector res; diff --git a/src/cx/util.h b/src/cx/util.h index f56aacbe..5f54799a 100644 --- a/src/cx/util.h +++ b/src/cx/util.h @@ -19,7 +19,8 @@ #include "common/model/namespace.h" -#include +#include "common/model/template_parameter.h" + #include namespace clanguml::cx::util { @@ -27,8 +28,8 @@ namespace clanguml::cx::util { std::pair split_ns( const std::string &full_name); -std::vector -parse_unexposed_template_params(const std::string ¶ms, +std::vector parse_unexposed_template_params( + const std::string ¶ms, const std::function &ns_resolve, int depth = 0); diff --git a/src/sequence_diagram/model/participant.cc b/src/sequence_diagram/model/participant.cc index b898d818..aa1ef0c4 100644 --- a/src/sequence_diagram/model/participant.cc +++ b/src/sequence_diagram/model/participant.cc @@ -20,83 +20,6 @@ namespace clanguml::sequence_diagram::model { -std::ostringstream &template_trait::render_template_params( - std::ostringstream &ostr, const common::model::namespace_ &using_namespace, - bool relative) const -{ - using clanguml::common::model::namespace_; - - if (!templates_.empty()) { - std::vector tnames; - std::vector tnames_simplified; - - std::transform(templates_.cbegin(), templates_.cend(), - std::back_inserter(tnames), - [ns = using_namespace, relative]( - const auto &tmplt) { return tmplt.to_string(ns, relative); }); - - ostr << fmt::format("<{}>", fmt::join(tnames, ",")); - } - - return ostr; -} - -void template_trait::set_base_template(const std::string &full_name) -{ - base_template_full_name_ = full_name; -} - -std::string template_trait::base_template() const -{ - return base_template_full_name_; -} - -void template_trait::add_template( - class_diagram::model::template_parameter tmplt) -{ - templates_.push_back(std::move(tmplt)); -} - -bool template_trait::is_implicit() const { return is_implicit_; } - -void template_trait::set_implicit(bool implicit) { is_implicit_ = implicit; } - -const std::vector & -template_trait::templates() const -{ - return templates_; -} - -int template_trait::calculate_template_specialization_match( - const template_trait &other, const std::string & /*full_name*/) const -{ - int res{}; - - // TODO: handle variadic templates - if (templates().size() != other.templates().size()) { - return res; - } - - // Iterate over all template arguments - for (auto i = 0U; i < other.templates().size(); i++) { - const auto &template_arg = templates().at(i); - const auto &other_template_arg = other.templates().at(i); - - if (template_arg == other_template_arg) { - res++; - } - else if (other_template_arg.is_specialization_of(template_arg)) { - continue; - } - else { - res = 0; - break; - } - } - - return res; -} - std::string participant::to_string() const { return fmt::format( diff --git a/src/sequence_diagram/model/participant.h b/src/sequence_diagram/model/participant.h index 25275927..9a3e19a3 100644 --- a/src/sequence_diagram/model/participant.h +++ b/src/sequence_diagram/model/participant.h @@ -17,39 +17,16 @@ */ #pragma once -#include "class_diagram/model/template_parameter.h" #include "common/model/element.h" +#include "common/model/template_parameter.h" +#include "common/model/template_trait.h" #include #include namespace clanguml::sequence_diagram::model { -struct template_trait { - std::ostringstream &render_template_params(std::ostringstream &ostr, - const common::model::namespace_ &using_namespace, bool relative) const; - - void set_base_template(const std::string &full_name); - - std::string base_template() const; - - void add_template(class_diagram::model::template_parameter tmplt); - - const std::vector & - templates() const; - - int calculate_template_specialization_match( - const template_trait &other, const std::string &full_name) const; - - bool is_implicit() const; - - void set_implicit(bool implicit); - -private: - std::vector templates_; - std::string base_template_full_name_; - bool is_implicit_{false}; -}; +using clanguml::common::model::template_trait; struct participant : public common::model::element, public common::model::stylable_element { diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index 5073fd8a..32581c6b 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -1336,7 +1336,7 @@ bool translation_unit_visitor::process_template_parameters( const clang::TemplateDecl &template_declaration, sequence_diagram::model::template_trait &c) { - using class_diagram::model::template_parameter; + using common::model::template_parameter; LOG_TRACE("Processing class {} template parameters...", common::get_qualified_name(template_declaration)); @@ -1357,7 +1357,7 @@ bool translation_unit_visitor::process_template_parameters( ct.set_default_value(""); ct.is_variadic(template_type_parameter->isParameterPack()); - c.add_template(ct); + c.add_template(std::move(ct)); } else if (clang::dyn_cast_or_null( parameter) != nullptr) { @@ -1371,7 +1371,7 @@ bool translation_unit_visitor::process_template_parameters( ct.set_default_value(""); ct.is_variadic(template_nontype_parameter->isParameterPack()); - c.add_template(ct); + c.add_template(std::move(ct)); } else if (clang::dyn_cast_or_null( parameter) != nullptr) { @@ -1385,7 +1385,7 @@ bool translation_unit_visitor::process_template_parameters( ct.set_default_value(""); ct.is_variadic(template_template_parameter->isParameterPack()); - c.add_template(ct); + c.add_template(std::move(ct)); } else { // pass @@ -1510,7 +1510,7 @@ void translation_unit_visitor:: { for (const auto &arg : template_args) { const auto argument_kind = arg.getKind(); - class_diagram::model::template_parameter argument; + common::model::template_parameter argument; if (argument_kind == clang::TemplateArgument::Template) { build_template_instantiation_process_template_argument( arg, argument); @@ -1535,14 +1535,14 @@ void translation_unit_visitor:: simplify_system_template( argument, argument.to_string(config().using_namespace(), false)); - template_instantiation.add_template(argument); + template_instantiation.add_template(std::move(argument)); } } void translation_unit_visitor:: build_template_instantiation_process_template_argument( const clang::TemplateArgument &arg, - class_diagram::model::template_parameter &argument) const + common::model::template_parameter &argument) const { argument.is_template_parameter(true); auto arg_name = @@ -1553,7 +1553,7 @@ void translation_unit_visitor:: void translation_unit_visitor:: build_template_instantiation_process_integral_argument( const clang::TemplateArgument &arg, - class_diagram::model::template_parameter &argument) const + common::model::template_parameter &argument) const { assert(arg.getKind() == clang::TemplateArgument::Integral); @@ -1564,7 +1564,7 @@ void translation_unit_visitor:: void translation_unit_visitor:: build_template_instantiation_process_expression_argument( const clang::TemplateArgument &arg, - class_diagram::model::template_parameter &argument) const + common::model::template_parameter &argument) const { assert(arg.getKind() == clang::TemplateArgument::Expression); @@ -1579,7 +1579,7 @@ void translation_unit_visitor:: const std::string & /*full_template_specialization_name*/, const clang::TemplateDecl *template_decl, const clang::TemplateArgument &arg, - class_diagram::model::template_parameter &argument) const + common::model::template_parameter &argument) const { assert(arg.getKind() == clang::TemplateArgument::Type); @@ -1596,7 +1596,7 @@ void translation_unit_visitor:: const clang::TemplateDecl *template_decl, const clang::TemplateArgument &arg, model::template_trait &template_instantiation, - class_diagram::model::template_parameter &argument) + common::model::template_parameter &argument) { assert(arg.getKind() == clang::TemplateArgument::Type); @@ -1685,7 +1685,7 @@ void translation_unit_visitor::process_template_specialization_argument( const auto argument_kind = arg.getKind(); if (argument_kind == clang::TemplateArgument::Type) { - class_diagram::model::template_parameter argument; + common::model::template_parameter argument; argument.is_template_parameter(false); // If this is a nested template type - add nested templates as @@ -1816,23 +1816,23 @@ void translation_unit_visitor::process_template_specialization_argument( simplify_system_template( argument, argument.to_string(config().using_namespace(), false)); - template_instantiation.add_template(argument); + template_instantiation.add_template(std::move(argument)); } else if (argument_kind == clang::TemplateArgument::Integral) { - class_diagram::model::template_parameter argument; + common::model::template_parameter argument; argument.is_template_parameter(false); argument.set_type(std::to_string(arg.getAsIntegral().getExtValue())); - template_instantiation.add_template(argument); + template_instantiation.add_template(std::move(argument)); } else if (argument_kind == clang::TemplateArgument::Expression) { - class_diagram::model::template_parameter argument; + common::model::template_parameter argument; argument.is_template_parameter(false); argument.set_type(common::get_source_text( arg.getAsExpr()->getSourceRange(), source_manager())); - template_instantiation.add_template(argument); + template_instantiation.add_template(std::move(argument)); } else if (argument_kind == clang::TemplateArgument::TemplateExpansion) { - class_diagram::model::template_parameter argument; + common::model::template_parameter argument; argument.is_template_parameter(true); cls->getLocation().dump(source_manager()); @@ -2040,8 +2040,7 @@ translation_unit_visitor::build_template_instantiation( void translation_unit_visitor:: process_unexposed_template_specialization_parameters( - const std::string &type_name, - class_diagram::model::template_parameter &tp, + const std::string &type_name, common::model::template_parameter &tp, model::class_ & /*c*/) const { auto template_params = cx::util::parse_unexposed_template_params( @@ -2053,8 +2052,7 @@ void translation_unit_visitor:: } bool translation_unit_visitor::simplify_system_template( - class_diagram::model::template_parameter &ct, - const std::string &full_name) const + common::model::template_parameter &ct, const std::string &full_name) const { if (config().type_aliases().count(full_name) > 0) { ct.set_name(config().type_aliases().at(full_name)); diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.h b/src/sequence_diagram/visitor/translation_unit_visitor.h index a094559c..c67a1d75 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.h +++ b/src/sequence_diagram/visitor/translation_unit_visitor.h @@ -30,6 +30,8 @@ namespace clanguml::sequence_diagram::visitor { +using common::model::template_parameter; + std::string to_string(const clang::FunctionTemplateDecl *decl); class translation_unit_visitor @@ -179,7 +181,7 @@ private: bool process_template_parameters( const clang::TemplateDecl &template_declaration, - sequence_diagram::model::template_trait &c); + common::model::template_trait &c); std::unique_ptr build_function_template_instantiation(const clang::FunctionDecl &pDecl); @@ -200,22 +202,22 @@ private: void build_template_instantiation_process_template_argument( const clang::TemplateArgument &arg, - class_diagram::model::template_parameter &argument) const; + common::model::template_parameter &argument) const; void build_template_instantiation_process_integral_argument( const clang::TemplateArgument &arg, - class_diagram::model::template_parameter &argument) const; + common::model::template_parameter &argument) const; void build_template_instantiation_process_expression_argument( const clang::TemplateArgument &arg, - class_diagram::model::template_parameter &argument) const; + common::model::template_parameter &argument) const; void build_template_instantiation_process_tag_argument( model::template_trait &template_instantiation, const std::string &full_template_specialization_name, const clang::TemplateDecl *template_decl, const clang::TemplateArgument &arg, - class_diagram::model::template_parameter &argument) const; + common::model::template_parameter &argument) const; void build_template_instantiation_process_type_argument( model::template_trait *parent, @@ -223,7 +225,7 @@ private: const clang::TemplateDecl *template_decl, const clang::TemplateArgument &arg, model::template_trait &template_instantiation, - class_diagram::model::template_parameter &argument); + common::model::template_parameter &argument); std::unique_ptr process_template_specialization( clang::ClassTemplateSpecializationDecl *cls); @@ -235,14 +237,14 @@ private: bool in_parameter_pack = false); void process_unexposed_template_specialization_parameters( - const std::string &type_name, - class_diagram::model::template_parameter &tp, model::class_ &c) const; + const std::string &type_name, common::model::template_parameter &tp, + model::class_ &c) const; std::unique_ptr build_template_instantiation( const clang::TemplateSpecializationType &template_type_decl, model::class_ *parent); - bool simplify_system_template(class_diagram::model::template_parameter &ct, + bool simplify_system_template(common::model::template_parameter &ct, const std::string &full_name) const; std::string simplify_system_template(const std::string &full_name) const; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 19326ed0..6edf1c76 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -19,7 +19,6 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} ${LIBCLANG_CXXFLAGS} ${TEST_DISAB if(APPLE) # Without this, clang-uml test cases fail with error saying that clang cannot find stdarg.h - #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${LLVM_LIBRARY_DIR}/clang/${LLVM_PACKAGE_VERSION}/include") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -I${LLVM_LIBRARY_DIR}/clang/${LLVM_PACKAGE_VERSION}/include") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -I${LLVM_LIBRARY_DIR}/clang/${LLVM_PACKAGE_VERSION}/include") endif(APPLE) diff --git a/tests/t00051/test_case.h b/tests/t00051/test_case.h index edd2a731..2e573b29 100644 --- a/tests/t00051/test_case.h +++ b/tests/t00051/test_case.h @@ -40,8 +40,8 @@ TEST_CASE("t00051", "[test-case][class]") REQUIRE_THAT(puml, IsInnerClass(_A("A"), _A("A::custom_thread2"))); REQUIRE_THAT(puml, - (IsMethod( - "custom_thread1", "void", "Function && f, Args &&... args"))); + (IsMethod("custom_thread1", "void", + "Function && f, Args &&... args"))); REQUIRE_THAT(puml, (IsMethod("thread", "void", "(lambda at ../../tests/t00051/t00051.cc:59:27) && "))); diff --git a/tests/t00052/.clang-uml b/tests/t00052/.clang-uml new file mode 100644 index 00000000..c02dbe15 --- /dev/null +++ b/tests/t00052/.clang-uml @@ -0,0 +1,12 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t00052_class: + type: class + glob: + - ../../tests/t00052/t00052.cc + include: + namespaces: + - clanguml::t00052 + using_namespace: + - clanguml::t00052 \ No newline at end of file diff --git a/tests/t00052/t00052.cc b/tests/t00052/t00052.cc new file mode 100644 index 00000000..5a75948e --- /dev/null +++ b/tests/t00052/t00052.cc @@ -0,0 +1,19 @@ +namespace clanguml { +namespace t00052 { + +class A { +public: + template T a(T p) { return p; } + + template void aa(F &&f, Q q) { f(q); } +}; + +template class B { +public: + T b(T t) { return t; } + + template T bb(F &&f, T t) { return f(t); } +}; + +} +} \ No newline at end of file diff --git a/tests/t00052/test_case.h b/tests/t00052/test_case.h new file mode 100644 index 00000000..e7636176 --- /dev/null +++ b/tests/t00052/test_case.h @@ -0,0 +1,50 @@ +/** + * tests/t00052/test_case.h + * + * Copyright (c) 2021-2023 Bartek Kryza + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +TEST_CASE("t00052", "[test-case][class]") +{ + auto [config, db] = load_config("t00052"); + + auto diagram = config.diagrams["t00052_class"]; + + REQUIRE(diagram->name == "t00052_class"); + + auto model = generate_class_diagram(*db, diagram); + + REQUIRE(model->name() == "t00052_class"); + + auto puml = generate_class_puml(diagram, *model); + AliasMatcher _A(puml); + + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); + + // Check if all classes exist + REQUIRE_THAT(puml, IsClass(_A("A"))); + + // Check if class templates exist + REQUIRE_THAT(puml, IsClassTemplate("B", "T")); + + // Check if all methods exist + REQUIRE_THAT(puml, (IsMethod("a", "T", "T p"))); + REQUIRE_THAT(puml, (IsMethod("aa", "void", "F && f, Q q"))); + REQUIRE_THAT(puml, (IsMethod("b", "T", "T t"))); + REQUIRE_THAT(puml, (IsMethod("bb", "T", "F && f, T t"))); + + save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml); +} \ No newline at end of file diff --git a/tests/test_cases.cc b/tests/test_cases.cc index 3fd9bce1..8d94c572 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -245,6 +245,7 @@ using namespace clanguml::test::matchers; #include "t00049/test_case.h" #include "t00050/test_case.h" #include "t00051/test_case.h" +#include "t00052/test_case.h" /// /// Sequence diagram tests diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index 105de191..117ff78a 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -150,6 +150,9 @@ test_cases: - name: t00051 title: Test case for relative paths in lambda names description: + - name: t00052 + title: Test case for template methods rendering + description: Sequence diagrams: - name: t20001 title: Basic sequence diagram test case