From 16195bfa6235a2aa6b4d904eca1dfe07ac1b3214 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Sun, 14 Jan 2024 13:38:08 +0100 Subject: [PATCH] Refactored template_builder to common namespace (#227) --- src/class_diagram/model/class.cc | 27 +- src/class_diagram/model/class.h | 49 +--- .../visitor/translation_unit_visitor.cc | 153 +++++++--- .../visitor/translation_unit_visitor.h | 24 +- src/common/clang_utils.cc | 53 ++++ src/common/clang_utils.h | 13 + src/common/model/template_element.cc | 52 ++++ src/common/model/template_element.h | 79 +++++ .../visitor/template_builder.cc | 271 +++++++----------- .../visitor/template_builder.h | 130 ++++++--- src/common/visitor/translation_unit_visitor.h | 17 +- src/sequence_diagram/model/diagram.h | 2 +- src/sequence_diagram/model/participant.h | 9 +- .../visitor/translation_unit_visitor.cc | 1 + src/util/util.h | 11 + tests/t00044/t00044.cc | 4 +- tests/t00044/test_case.h | 3 +- tests/test_cases.h | 6 + 18 files changed, 545 insertions(+), 359 deletions(-) create mode 100644 src/common/model/template_element.cc create mode 100644 src/common/model/template_element.h rename src/{class_diagram => common}/visitor/template_builder.cc (82%) rename src/{class_diagram => common}/visitor/template_builder.h (78%) diff --git a/src/class_diagram/model/class.cc b/src/class_diagram/model/class.cc index 8105ee19..4dc8c0fc 100644 --- a/src/class_diagram/model/class.cc +++ b/src/class_diagram/model/class.cc @@ -25,7 +25,7 @@ namespace clanguml::class_diagram::model { class_::class_(const common::model::namespace_ &using_namespace) - : element{using_namespace} + : template_element{using_namespace} { } @@ -33,10 +33,6 @@ bool class_::is_struct() const { return is_struct_; } void class_::is_struct(bool is_struct) { is_struct_ = is_struct; } -bool class_::is_template() const { return is_template_; } - -void class_::is_template(bool is_template) { is_template_ = is_template; } - bool class_::is_union() const { return is_union_; } void class_::is_union(bool is_union) { is_union_ = is_union; } @@ -115,27 +111,6 @@ bool class_::is_abstract() const [](const auto &method) { return method.is_pure_virtual(); }); } -int class_::calculate_template_specialization_match(const class_ &other) const -{ - int res{0}; - - if (name_and_ns() != other.name_and_ns()) { - return res; - } - - return template_trait::calculate_template_specialization_match(other); -} - -void class_::template_specialization_found(bool found) -{ - template_specialization_found_ = found; -} - -bool class_::template_specialization_found() const -{ - return template_specialization_found_; -} - std::optional class_::doxygen_link() const { const auto *type = is_struct() ? "struct" : "class"; diff --git a/src/class_diagram/model/class.h b/src/class_diagram/model/class.h index c3484d43..b8375aec 100644 --- a/src/class_diagram/model/class.h +++ b/src/class_diagram/model/class.h @@ -20,9 +20,9 @@ #include "class_member.h" #include "class_method.h" #include "class_parent.h" -#include "common/model/element.h" #include "common/model/enums.h" #include "common/model/stylable_element.h" +#include "common/model/template_element.h" #include "common/model/template_parameter.h" #include "common/model/template_trait.h" #include "common/types.h" @@ -35,9 +35,8 @@ namespace clanguml::class_diagram::model { /** * @brief Diagram element representing a class or class template. */ -class class_ : public common::model::element, - public common::model::stylable_element, - public template_trait { +class class_ : public common::model::template_element, + public common::model::stylable_element { public: class_(const common::model::namespace_ &using_namespace); @@ -69,20 +68,6 @@ public: */ void is_struct(bool is_struct); - /** - * Whether or not the class is a template. - * - * @return True, if the class is a template. - */ - bool is_template() const; - - /** - * Set, whether the class is a template. - * - * @param is_struct True, if the class is a template. - */ - void is_template(bool is_template); - /** * Whether or not the class is a union. * @@ -171,31 +156,6 @@ public: */ bool is_abstract() const; - /** - * @brief Calculate template specialization match with other class. - * - * This method is a wrapper over - * @ref template_trait::calculate_template_specialization_match() - * - * @param other - * @return - */ - int calculate_template_specialization_match(const class_ &other) const; - - /** - * Whether, a template specialization has already been found for this class. - * @return True, if a template specialization has already been found. - */ - bool template_specialization_found() const; - - /** - * Set, whether a template specialization has already been found for this - * class. - * - * @param found True, if a template specialization has already been found. - */ - void template_specialization_found(bool found); - /** * @brief Generate Doxygen style HTML link for the class. * @@ -208,15 +168,12 @@ public: private: bool is_struct_{false}; - bool is_template_{false}; bool is_union_{false}; std::vector members_; std::vector methods_; std::vector bases_; std::string base_template_full_name_; std::string full_name_; - - bool template_specialization_found_{false}; }; } // namespace clanguml::class_diagram::model diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index cac07180..af0a154d 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -32,7 +32,26 @@ translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm, : common::visitor::translation_unit_visitor{sm, config} , diagram_{diagram} , config_{config} - , template_builder_{*this} + , template_builder_{diagram_, config_, *this, + [uns = config_.using_namespace()](const clang::NamedDecl *decl) { + auto cls = std::make_unique(uns); + cls->is_struct(common::is_struct(decl)); + return cls; + }, + [this](common::model::template_element &template_instantiation_base, + const std::string &full_name, common::id_t templated_decl_id) { + find_instantiation_relationships( + template_instantiation_base, full_name, templated_decl_id); + }, + [](clanguml::common::model::template_element &tinst, + clanguml::common::id_t id, const std::string &full_name) { + model::class_parent cp; + cp.set_access(common::model::access_t::kPublic); + cp.set_name(full_name); + cp.set_id(id); + + dynamic_cast(tinst).add_parent(std::move(cp)); + }} { } @@ -254,7 +273,9 @@ bool translation_unit_visitor::VisitTypeAliasTemplateDecl( return true; auto template_specialization_ptr = - tbuilder().build(cls, *template_type_specialization_ptr); + std::make_unique(config().using_namespace()); + tbuilder().build( + *template_specialization_ptr, cls, *template_type_specialization_ptr); if (!template_specialization_ptr) return true; @@ -1060,7 +1081,9 @@ void translation_unit_visitor::process_class_bases( else if (const auto *tsp = base.getType()->getAs(); tsp != nullptr) { - auto template_specialization_ptr = tbuilder().build(cls, *tsp, {}); + auto template_specialization_ptr = + std::make_unique(config().using_namespace()); + tbuilder().build(*template_specialization_ptr, cls, *tsp, {}); if (template_specialization_ptr) { cp.set_id(template_specialization_ptr->id()); } @@ -1261,7 +1284,7 @@ void translation_unit_visitor::process_method( auto method_return_type = common::to_string(mf.getReturnType(), mf.getASTContext()); - ensure_lambda_type_is_relative(method_return_type); + common::ensure_lambda_type_is_relative(config(), method_return_type); auto method_name = mf.getNameAsString(); if (mf.isTemplated()) { @@ -1304,7 +1327,9 @@ void translation_unit_visitor::process_method( ->getAs(); if (unaliased_type != nullptr) { - auto template_specialization_ptr = tbuilder().build( + auto template_specialization_ptr = + std::make_unique(config().using_namespace()); + tbuilder().build(*template_specialization_ptr, unaliased_type->getTemplateName().getAsTemplateDecl(), *unaliased_type, &c); @@ -1652,7 +1677,7 @@ void translation_unit_visitor::process_function_parameter( auto parameter_type = common::to_string(p.getType(), p.getASTContext()); // Is there no better way to determine that 'type' is a lambda? - ensure_lambda_type_is_relative(parameter_type); + common::ensure_lambda_type_is_relative(config(), parameter_type); parameter.set_type(parameter_type); @@ -1678,7 +1703,9 @@ void translation_unit_visitor::process_function_parameter( .getUnqualifiedType() ->getAs(); templ != nullptr) { - auto template_specialization_ptr = tbuilder().build( + auto template_specialization_ptr = + std::make_unique(config().using_namespace()); + tbuilder().build(*template_specialization_ptr, templ->getTemplateName().getAsTemplateDecl(), *templ, &c); if (diagram().should_include( @@ -1711,40 +1738,6 @@ void translation_unit_visitor::process_function_parameter( method.add_parameter(std::move(parameter)); } -void translation_unit_visitor::ensure_lambda_type_is_relative( - std::string ¶meter_type) const -{ -#ifdef _MSC_VER - auto root_name = - fmt::format("{}", std::filesystem::current_path().root_name().string()); -#else - auto root_name = std::string{"/"}; -#endif - - std::string lambda_prefix{fmt::format("(lambda at {}", root_name)}; - - while (parameter_type.find(lambda_prefix) != std::string::npos) { - auto lambda_begin = parameter_type.find(lambda_prefix); - auto lambda_prefix_size = lambda_prefix.size(); -#ifdef _MSC_VER - // Skip the `\` or `/` after drive letter and semicolon - lambda_prefix_size++; -#endif - auto absolute_lambda_path_end = - parameter_type.find(':', lambda_begin + lambda_prefix_size); - auto absolute_lambda_path = parameter_type.substr( - lambda_begin + lambda_prefix_size - 1, - absolute_lambda_path_end - (lambda_begin + lambda_prefix_size - 1)); - - auto relative_lambda_path = util::path_to_url( - config().make_path_relative(absolute_lambda_path).string()); - - parameter_type = fmt::format("{}(lambda at {}{}", - parameter_type.substr(0, lambda_begin), relative_lambda_path, - parameter_type.substr(absolute_lambda_path_end)); - } -} - void translation_unit_visitor::add_relationships(class_ &c, const class_member &field, const found_relationships_t &relationships, bool break_on_first_aggregation) @@ -1816,7 +1809,8 @@ std::unique_ptr translation_unit_visitor::process_template_specialization( clang::ClassTemplateSpecializationDecl *cls) { - auto c_ptr = tbuilder().build_from_class_template_specialization(*cls); + auto c_ptr = std::make_unique(config().using_namespace()); + tbuilder().build_from_class_template_specialization(*c_ptr, *cls); auto &template_instantiation = *c_ptr; template_instantiation.is_template(true); @@ -1876,7 +1870,7 @@ void translation_unit_visitor::process_field( auto field_type_str = common::to_string(field_type, field_declaration.getASTContext(), false); - ensure_lambda_type_is_relative(field_type_str); + common::ensure_lambda_type_is_relative(config(), field_type_str); class_member field{ common::access_specifier_to_access_t(field_declaration.getAccess()), @@ -1938,7 +1932,9 @@ void translation_unit_visitor::process_field( if (template_field_type != nullptr && !field_type_is_template_template_parameter) { // Build the template instantiation for the field type - auto template_specialization_ptr = tbuilder().build( + auto template_specialization_ptr = + std::make_unique(config().using_namespace()); + tbuilder().build(*template_specialization_ptr, field_type->getAs() ->getTemplateName() .getAsTemplateDecl(), @@ -2136,6 +2132,12 @@ bool translation_unit_visitor::has_processed_template_class( return util::contains(processed_template_qualified_names_, qualified_name); } +void translation_unit_visitor::add_diagram_element( + std::unique_ptr element) +{ + add_class(util::unique_pointer_cast(std::move(element))); +} + void translation_unit_visitor::add_class(std::unique_ptr &&c) { if ((config().generate_packages() && @@ -2220,4 +2222,67 @@ void translation_unit_visitor::add_concept(std::unique_ptr &&c) } } +void translation_unit_visitor::find_instantiation_relationships( + common::model::template_element &template_instantiation_base, + const std::string &full_name, common::id_t templated_decl_id) +{ + class_diagram::model::class_ &template_instantiation = + dynamic_cast( + template_instantiation_base); + + // First try to find the best match for this template in partially + // specialized templates + std::string destination{}; + std::string best_match_full_name{}; + auto full_template_name = template_instantiation.full_name(false); + int best_match{}; + common::id_t best_match_id{0}; + + for (const auto templ : diagram().classes()) { + if (templ.get() == template_instantiation) + continue; + + auto c_full_name = templ.get().full_name(false); + auto match = + template_instantiation.calculate_template_specialization_match( + templ.get()); + + if (match > best_match) { + best_match = match; + best_match_full_name = c_full_name; + best_match_id = templ.get().id(); + } + } + + auto templated_decl_global_id = + id_mapper().get_global_id(templated_decl_id).value_or(0); + + if (best_match_id > 0) { + destination = best_match_full_name; + template_instantiation.add_relationship( + {common::model::relationship_t::kInstantiation, best_match_id}); + template_instantiation.template_specialization_found(true); + } + // If we can't find optimal match for parent template specialization, + // just use whatever clang suggests + else if (diagram().has_element(templated_decl_global_id)) { + template_instantiation.add_relationship( + {common::model::relationship_t::kInstantiation, + templated_decl_global_id}); + template_instantiation.template_specialization_found(true); + } + else if (diagram().should_include(common::model::namespace_{full_name})) { + LOG_DBG("Skipping instantiation relationship from {} to {}", + template_instantiation.full_name(false), templated_decl_global_id); + } + else { + LOG_DBG("== Cannot determine global id for specialization template {} " + "- delaying until the translation unit is complete ", + templated_decl_global_id); + + template_instantiation.add_relationship( + {common::model::relationship_t::kInstantiation, templated_decl_id}); + } +} + } // namespace clanguml::class_diagram::visitor diff --git a/src/class_diagram/visitor/translation_unit_visitor.h b/src/class_diagram/visitor/translation_unit_visitor.h index 30e7ea65..ac96229b 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.h +++ b/src/class_diagram/visitor/translation_unit_visitor.h @@ -20,13 +20,11 @@ #include "class_diagram/model/class.h" #include "class_diagram/model/concept.h" #include "class_diagram/model/diagram.h" -#include "class_diagram/visitor/template_builder.h" #include "common/model/enums.h" #include "common/model/template_trait.h" -#include "common/visitor/ast_id_mapper.h" +#include "common/visitor/template_builder.h" #include "common/visitor/translation_unit_visitor.h" #include "config/config.h" -#include "template_builder.h" #include #include @@ -53,6 +51,8 @@ using clanguml::common::model::relationship; using clanguml::common::model::relationship_t; using clanguml::common::model::template_parameter; using clanguml::common::model::template_trait; +using clanguml::common::visitor::found_relationships_t; +using clanguml::common::visitor::template_builder; /** * @brief Class diagram translation unit visitor @@ -136,13 +136,6 @@ public: */ void finalize(); - /** - * @brief Get reference to Clang AST to clang-uml id mapper - * - * @return Reference to Clang AST to clang-uml id mapper - */ - common::visitor::ast_id_mapper &id_mapper() const { return id_mapper_; } - /** * @brief Add class (or template class) to the diagram. * @@ -164,8 +157,6 @@ public: */ void add_concept(std::unique_ptr &&c); - void ensure_lambda_type_is_relative(std::string ¶meter_type) const; - private: /** * @brief Check if the diagram should include a declaration. @@ -460,6 +451,13 @@ private: */ bool has_processed_template_class(const std::string &qualified_name) const; + void add_diagram_element( + std::unique_ptr element) override; + + void find_instantiation_relationships( + common::model::template_element &template_instantiation_base, + const std::string &full_name, common::id_t templated_decl_id); + /** * @brief Get template builder reference * @@ -473,8 +471,6 @@ private: // Reference to class diagram config const clanguml::config::class_diagram &config_; - mutable common::visitor::ast_id_mapper id_mapper_; - template_builder template_builder_; std::map(body); } +bool is_struct(const clang::NamedDecl *decl) +{ + if (decl == nullptr) + return false; + + if (const clang::CXXRecordDecl *record = + clang::dyn_cast(decl); + record) { + return record->isStruct(); + } + + if (const clang::TagDecl *tag = clang::dyn_cast(decl); + tag) { + return tag->isStruct(); + } + + return false; +} + } // namespace clanguml::common diff --git a/src/common/clang_utils.h b/src/common/clang_utils.h index 2f1ef2ab..565a6eae 100644 --- a/src/common/clang_utils.h +++ b/src/common/clang_utils.h @@ -17,9 +17,11 @@ */ #pragma once +// #include "class_diagram/model/diagram.h" #include "common/model/enums.h" #include "common/model/namespace.h" #include "common/model/template_parameter.h" +#include "config/config.h" #include "types.h" #include "util/util.h" @@ -153,6 +155,9 @@ std::string get_source_text( std::tuple extract_template_parameter_index(const std::string &type_parameter); +void ensure_lambda_type_is_relative( + const config::diagram &config, std::string ¶meter_type); + /** * @brief Check if an expression is contained in another expression * @@ -303,4 +308,12 @@ clang::RawComment *get_expression_raw_comment(const clang::SourceManager &sm, */ bool is_coroutine(const clang::FunctionDecl &decl); +/** + * Check if named declaration is a C++ struct. + * + * @param decl Declaration to check + * @return True, if declaration represents a struct. + */ +bool is_struct(const clang::NamedDecl *decl); + } // namespace clanguml::common diff --git a/src/common/model/template_element.cc b/src/common/model/template_element.cc new file mode 100644 index 00000000..2caac028 --- /dev/null +++ b/src/common/model/template_element.cc @@ -0,0 +1,52 @@ +/** + * @file src/common/model/template_element.cc + * + * Copyright (c) 2021-2024 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 "template_element.h" + +namespace clanguml::common::model { + +bool template_element::is_template() const { return is_template_; } + +void template_element::is_template(bool is_template) +{ + is_template_ = is_template; +} + +int template_element::calculate_template_specialization_match( + const template_element &other) const +{ + int res{0}; + + if (name_and_ns() != other.name_and_ns()) { + return res; + } + + return template_trait::calculate_template_specialization_match(other); +} + +void template_element::template_specialization_found(bool found) +{ + template_specialization_found_ = found; +} + +bool template_element::template_specialization_found() const +{ + return template_specialization_found_; +} + +} // namespace clanguml::common::model \ No newline at end of file diff --git a/src/common/model/template_element.h b/src/common/model/template_element.h new file mode 100644 index 00000000..ad70eaab --- /dev/null +++ b/src/common/model/template_element.h @@ -0,0 +1,79 @@ +/** + * @file src/common/model/template_element.h + * + * Copyright (c) 2021-2024 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 "element.h" +#include "template_trait.h" + +namespace clanguml::common::model { + +/** + * @brief Base class for any element qualified by namespace. + */ +class template_element : public element, public template_trait { +public: + using element::element; + + virtual ~template_element() = default; + + /** + * Whether or not the class is a template. + * + * @return True, if the class is a template. + */ + bool is_template() const; + + /** + * Set, whether the class is a template. + * + * @param is_struct True, if the class is a template. + */ + void is_template(bool is_template); + + /** + * @brief Calculate template specialization match with other class. + * + * This method is a wrapper over + * @ref template_trait::calculate_template_specialization_match() + * + * @param other + * @return + */ + int calculate_template_specialization_match( + const template_element &other) const; + + /** + * Whether, a template specialization has already been found for this class. + * @return True, if a template specialization has already been found. + */ + bool template_specialization_found() const; + + /** + * Set, whether a template specialization has already been found for this + * class. + * + * @param found True, if a template specialization has already been found. + */ + void template_specialization_found(bool found); + +private: + bool template_specialization_found_{false}; + bool is_template_{false}; +}; + +} \ No newline at end of file diff --git a/src/class_diagram/visitor/template_builder.cc b/src/common/visitor/template_builder.cc similarity index 82% rename from src/class_diagram/visitor/template_builder.cc rename to src/common/visitor/template_builder.cc index ae73733f..eee5ed7c 100644 --- a/src/class_diagram/visitor/template_builder.cc +++ b/src/common/visitor/template_builder.cc @@ -21,24 +21,29 @@ #include "translation_unit_visitor.h" #include -namespace clanguml::class_diagram::visitor { +namespace clanguml::common::visitor { -template_builder::template_builder( - clanguml::class_diagram::visitor::translation_unit_visitor &visitor) - : diagram_{visitor.diagram()} - , config_{visitor.config()} +template_builder::template_builder(clanguml::common::model::diagram &diagram_, + const clanguml::config::diagram &config_, + clanguml::common::visitor::translation_unit_visitor &visitor, + element_factory_t element_factory, + find_instantiation_relationships_t find_instantiation_relationships, + on_argument_base_found_t on_argument_base_found) + : diagram_{diagram_} + , config_{config_} , id_mapper_{visitor.id_mapper()} , source_manager_{visitor.source_manager()} , visitor_{visitor} + , element_factory_{std::move(element_factory)} + , find_instantiation_relationships_{std::move( + find_instantiation_relationships)} + , on_argument_base_found_{std::move(on_argument_base_found)} { } -class_diagram::model::diagram &template_builder::diagram() { return diagram_; } +common::model::diagram &template_builder::diagram() { return diagram_; } -const config::class_diagram &template_builder::config() const -{ - return config_; -} +const config::diagram &template_builder::config() const { return config_; } const namespace_ &template_builder::using_namespace() const { @@ -70,9 +75,11 @@ bool template_builder::simplify_system_template( return false; } -std::unique_ptr template_builder::build(const clang::NamedDecl *cls, +void template_builder::build( + clanguml::common::model::template_element &template_instantiation, + const clang::NamedDecl *cls, const clang::TemplateSpecializationType &template_type_decl, - std::optional parent) + std::optional parent) { // // Here we'll hold the template base class params to replace with the @@ -94,13 +101,6 @@ std::unique_ptr template_builder::build(const clang::NamedDecl *cls, const auto &template_type = *template_type_ptr; - // - // Create class_ instance to hold the template instantiation - // - auto template_instantiation_ptr = - std::make_unique(using_namespace()); - - auto &template_instantiation = *template_instantiation_ptr; template_instantiation.is_template(true); std::string full_template_specialization_name = common::to_string( @@ -210,78 +210,21 @@ std::unique_ptr template_builder::build(const clang::NamedDecl *cls, template_type.template_arguments(), template_instantiation, template_decl); - // First try to find the best match for this template in partially - // specialized templates - std::string destination{}; - std::string best_match_full_name{}; - auto full_template_name = template_instantiation.full_name(false); - int best_match{}; - common::id_t best_match_id{0}; - - for (const auto templ : diagram().classes()) { - if (templ.get() == template_instantiation) - continue; - - auto c_full_name = templ.get().full_name(false); - auto match = - template_instantiation.calculate_template_specialization_match( - templ.get()); - - if (match > best_match) { - best_match = match; - best_match_full_name = c_full_name; - best_match_id = templ.get().id(); - } - } - - auto templated_decl_id = - template_type.getTemplateName().getAsTemplateDecl()->getID(); - auto templated_decl_global_id = - id_mapper().get_global_id(templated_decl_id).value_or(0); - - if (best_match_id > 0) { - destination = best_match_full_name; - template_instantiation.add_relationship( - {relationship_t::kInstantiation, best_match_id}); - template_instantiation.template_specialization_found(true); - } - // If we can't find optimal match for parent template specialization, - // just use whatever clang suggests - else if (diagram().has_element(templated_decl_global_id)) { - template_instantiation.add_relationship( - {relationship_t::kInstantiation, templated_decl_global_id}); - template_instantiation.template_specialization_found(true); - } - else if (diagram().should_include( - namespace_{full_template_specialization_name})) { - LOG_DBG("Skipping instantiation relationship from {} to {}", - template_instantiation_ptr->full_name(false), templated_decl_id); - } - else { - LOG_DBG("== Cannot determine global id for specialization template {} " - "- delaying until the translation unit is complete ", - templated_decl_id); - - template_instantiation.add_relationship( - {relationship_t::kInstantiation, templated_decl_id}); - } + find_instantiation_relationships(template_instantiation, + template_type.getTemplateName().getAsTemplateDecl()->getID(), + full_template_specialization_name); template_instantiation.set_id( - common::to_id(template_instantiation_ptr->full_name(false))); + common::to_id(template_instantiation.full_name(false))); visitor_.set_source_location(*cls, template_instantiation); - - return template_instantiation_ptr; } -std::unique_ptr -template_builder::build_from_class_template_specialization( +void template_builder::build_from_class_template_specialization( + clanguml::common::model::template_element &template_instantiation, const clang::ClassTemplateSpecializationDecl &template_specialization, - std::optional parent) + std::optional parent) { - auto template_instantiation_ptr = - std::make_unique(config_.using_namespace()); - // // Here we'll hold the template base params to replace with the // instantiated values @@ -290,9 +233,6 @@ template_builder::build_from_class_template_specialization( /*is variadic */ bool>> template_base_params{}; - auto &template_instantiation = *template_instantiation_ptr; - template_instantiation.is_struct(template_specialization.isStruct()); - const clang::ClassTemplateDecl *template_decl = template_specialization.getSpecializedTemplate(); @@ -312,63 +252,29 @@ template_builder::build_from_class_template_specialization( template_instantiation.set_id( common::to_id(template_instantiation.full_name(false))); - // First try to find the best match for this template in partially - // specialized templates - std::string destination{}; - std::string best_match_full_name{}; - auto full_template_name = template_instantiation.full_name(false); - int best_match{}; - common::id_t best_match_id{0}; - - for (const auto templ : diagram().classes()) { - if (templ.get() == template_instantiation) - continue; - - auto c_full_name = templ.get().full_name(false); - auto match = - template_instantiation.calculate_template_specialization_match( - templ.get()); - - if (match > best_match) { - best_match = match; - best_match_full_name = c_full_name; - best_match_id = templ.get().id(); - } - } - - auto templated_decl_id = template_specialization.getID(); - auto templated_decl_local_id = - id_mapper().get_global_id(templated_decl_id).value_or(0); - - if (best_match_id > 0) { - destination = best_match_full_name; - template_instantiation.add_relationship( - {relationship_t::kInstantiation, best_match_id}); - template_instantiation.template_specialization_found(true); - } - else if (diagram().has_element(templated_decl_local_id)) { - // If we can't find optimal match for parent template specialization, - // just use whatever clang suggests - template_instantiation.add_relationship( - {relationship_t::kInstantiation, templated_decl_local_id}); - template_instantiation.template_specialization_found(true); - } - else if (diagram().should_include(namespace_{qualified_name})) { - LOG_DBG("Skipping instantiation relationship from {} to {}", - template_instantiation_ptr->full_name(false), templated_decl_id); - } + find_instantiation_relationships(template_instantiation, + template_specialization.getID(), qualified_name); visitor_.set_source_location(*template_decl, template_instantiation); +} - return template_instantiation_ptr; +void template_builder::find_instantiation_relationships( + common::model::template_element &template_instantiation, common::id_t id, + const std::string &qualified_name) const +{ + if (find_instantiation_relationships_) { + find_instantiation_relationships_( + template_instantiation, qualified_name, id); + } } void template_builder::process_template_arguments( - std::optional &parent, + std::optional &parent, const clang::NamedDecl *cls, std::deque> &template_base_params, const clang::ArrayRef &template_args, - class_ &template_instantiation, const clang::TemplateDecl *template_decl) + clanguml::common::model::template_element &template_instantiation, + const clang::TemplateDecl *template_decl) { auto arg_index{0}; @@ -445,8 +351,9 @@ void template_builder::process_template_arguments( } void template_builder::argument_process_dispatch( - std::optional &parent, - const clang::NamedDecl *cls, class_ &template_instantiation, + std::optional &parent, + const clang::NamedDecl *cls, + clanguml::common::model::template_element &template_instantiation, const clang::TemplateDecl *template_decl, const clang::TemplateArgument &arg, size_t argument_index, std::vector &argument) @@ -529,9 +436,11 @@ clang::QualType template_builder::consume_context( } template_parameter template_builder::process_type_argument( - std::optional &parent, + std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, - clang::QualType type, class_ &template_instantiation, size_t argument_index) + clang::QualType type, + clanguml::common::model::template_element &template_instantiation, + size_t argument_index) { std::optional argument; @@ -739,8 +648,9 @@ template_parameter template_builder::process_expression_argument( } std::vector template_builder::process_pack_argument( - std::optional &parent, - const clang::NamedDecl *cls, class_ &template_instantiation, + std::optional &parent, + const clang::NamedDecl *cls, + clanguml::common::model::template_element &template_instantiation, const clang::TemplateDecl *base_template_decl, const clang::TemplateArgument &arg, size_t argument_index, std::vector & /*argument*/) @@ -760,9 +670,10 @@ std::vector template_builder::process_pack_argument( } std::optional template_builder::try_as_member_pointer( - std::optional &parent, + std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, - clang::QualType &type, class_ &template_instantiation, + clang::QualType &type, + clanguml::common::model::template_element &template_instantiation, size_t argument_index) { const auto *mp_type = @@ -835,9 +746,10 @@ std::optional template_builder::try_as_member_pointer( } std::optional template_builder::try_as_array( - std::optional &parent, + std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, - clang::QualType &type, class_ &template_instantiation, + clang::QualType &type, + clanguml::common::model::template_element &template_instantiation, size_t argument_index) { const auto *array_type = common::dereference(type)->getAsArrayTypeUnsafe(); @@ -877,9 +789,10 @@ std::optional template_builder::try_as_array( } std::optional template_builder::try_as_function_prototype( - std::optional &parent, + std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, - clang::QualType &type, class_ &template_instantiation, + clang::QualType &type, + clanguml::common::model::template_element &template_instantiation, size_t argument_index) { const auto *function_type = type->getAs(); @@ -926,10 +839,11 @@ std::optional template_builder::try_as_function_prototype( } std::optional template_builder::try_as_decl_type( - std::optional & /*parent*/, + std::optional & /*parent*/, const clang::NamedDecl * /*cls*/, const clang::TemplateDecl * /*template_decl*/, clang::QualType &type, - class_ & /*template_instantiation*/, size_t /*argument_index*/) + clanguml::common::model::template_element & /*template_instantiation*/, + size_t /*argument_index*/) { const auto *decl_type = common::dereference(type)->getAs(); @@ -944,10 +858,11 @@ std::optional template_builder::try_as_decl_type( } std::optional template_builder::try_as_typedef_type( - std::optional &parent, + std::optional &parent, const clang::NamedDecl * /*cls*/, const clang::TemplateDecl * /*template_decl*/, clang::QualType &type, - class_ & /*template_instantiation*/, size_t /*argument_index*/) + clanguml::common::model::template_element & /*template_instantiation*/, + size_t /*argument_index*/) { const auto *typedef_type = common::dereference(type)->getAs(); @@ -978,9 +893,10 @@ std::optional template_builder::try_as_typedef_type( std::optional template_builder::try_as_template_specialization_type( - std::optional &parent, + std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, - clang::QualType &type, class_ &template_instantiation, + clang::QualType &type, + clanguml::common::model::template_element &template_instantiation, size_t argument_index) { const auto *nested_template_type = @@ -1019,7 +935,11 @@ template_builder::try_as_template_specialization_type( argument.set_type(nested_type_name); - auto nested_template_instantiation = build(cls, *nested_template_type, + auto nested_template_instantiation = + element_factory_(nested_template_type->getTemplateName() + .getAsTemplateDecl() + ->getTemplatedDecl()); + build(*nested_template_instantiation, cls, *nested_template_type, diagram().should_include( namespace_{template_decl->getQualifiedNameAsString()}) ? std::make_optional(&template_instantiation) @@ -1064,7 +984,7 @@ template_builder::try_as_template_specialization_type( if (diagram().should_include( namespace_{nested_template_instantiation_full_name})) { visitor_.set_source_location(*cls, *nested_template_instantiation); - visitor_.add_class(std::move(nested_template_instantiation)); + visitor_.add_diagram_element(std::move(nested_template_instantiation)); } return argument; @@ -1108,7 +1028,7 @@ std::optional template_builder::try_as_template_parm_type( argument.is_variadic(is_variadic); - visitor_.ensure_lambda_type_is_relative(type_parameter_name); + common::ensure_lambda_type_is_relative(config(), type_parameter_name); return argument; } @@ -1127,16 +1047,17 @@ std::optional template_builder::try_as_lambda( auto argument = template_parameter::make_argument(""); type = consume_context(type, argument); - visitor_.ensure_lambda_type_is_relative(type_name); + common::ensure_lambda_type_is_relative(config(), type_name); argument.set_type(type_name); return argument; } std::optional template_builder::try_as_record_type( - std::optional &parent, + std::optional &parent, const clang::NamedDecl * /*cls*/, const clang::TemplateDecl *template_decl, - clang::QualType &type, class_ &template_instantiation, + clang::QualType &type, + clanguml::common::model::template_element &template_instantiation, size_t /*argument_index*/) { const auto *record_type = @@ -1162,8 +1083,10 @@ std::optional template_builder::try_as_record_type( record_type->getAsRecordDecl()); if (class_template_specialization != nullptr) { - auto tag_argument = build_from_class_template_specialization( - *class_template_specialization); + auto tag_argument = element_factory_(class_template_specialization); + + build_from_class_template_specialization( + *tag_argument, *class_template_specialization); if (tag_argument) { argument.set_type(tag_argument->name_and_ns()); @@ -1179,7 +1102,7 @@ std::optional template_builder::try_as_record_type( parent.value()->add_relationship( {relationship_t::kDependency, tag_argument->id()}); visitor_.set_source_location(*template_decl, *tag_argument); - visitor_.add_class(std::move(tag_argument)); + visitor_.add_diagram_element(std::move(tag_argument)); } } } @@ -1198,9 +1121,10 @@ std::optional template_builder::try_as_record_type( } std::optional template_builder::try_as_enum_type( - std::optional & /*parent*/, + std::optional & /*parent*/, const clang::NamedDecl * /*cls*/, const clang::TemplateDecl *template_decl, - clang::QualType &type, class_ &template_instantiation) + clang::QualType &type, + clanguml::common::model::template_element &template_instantiation) { const auto *enum_type = type->getAs(); if (enum_type == nullptr) @@ -1226,7 +1150,7 @@ std::optional template_builder::try_as_enum_type( } std::optional template_builder::try_as_builtin_type( - std::optional & /*parent*/, + std::optional & /*parent*/, clang::QualType &type, const clang::TemplateDecl *template_decl) { const auto *builtin_type = type->getAs(); @@ -1244,16 +1168,19 @@ std::optional template_builder::try_as_builtin_type( return argument; } -bool template_builder::add_base_classes(class_ &tinst, +bool template_builder::add_base_classes( + clanguml::common::model::template_element &tinst, std::deque> &template_base_params, int arg_index, bool variadic_params, const template_parameter &ct) { bool add_template_argument_as_base_class = false; - auto [arg_name, index, is_variadic] = template_base_params.front(); - if (variadic_params) + if (variadic_params) { add_template_argument_as_base_class = true; + } else { + auto [arg_name, index, is_variadic] = template_base_params.front(); + variadic_params = is_variadic; if ((arg_index == index) || (is_variadic && arg_index >= index)) { add_template_argument_as_base_class = true; @@ -1268,13 +1195,9 @@ bool template_builder::add_base_classes(class_ &tinst, if (add_template_argument_as_base_class && maybe_id) { LOG_DBG("Adding template argument as base class '{}'", ct.to_string({}, false)); - - model::class_parent cp; - cp.set_access(common::model::access_t::kPublic); - cp.set_name(ct.to_string({}, false)); - cp.set_id(maybe_id.value()); - - tinst.add_parent(std::move(cp)); + if (on_argument_base_found_) + on_argument_base_found_( + tinst, maybe_id.value(), ct.to_string({}, false)); } return variadic_params; diff --git a/src/class_diagram/visitor/template_builder.h b/src/common/visitor/template_builder.h similarity index 78% rename from src/class_diagram/visitor/template_builder.h rename to src/common/visitor/template_builder.h index 1a2b58ff..07e16976 100644 --- a/src/class_diagram/visitor/template_builder.h +++ b/src/common/visitor/template_builder.h @@ -17,15 +17,15 @@ */ #pragma once -#include "class_diagram/model/class.h" -#include "class_diagram/model/concept.h" -#include "class_diagram/model/diagram.h" +#include "common/model/diagram.h" +#include "common/model/template_element.h" #include "common/visitor/ast_id_mapper.h" +#include "common/visitor/translation_unit_visitor.h" #include "config/config.h" -namespace clanguml::class_diagram::visitor { +namespace clanguml::common::visitor { -using class_diagram::model::class_; +// using class_diagram::model::class_; using common::model::namespace_; using common::model::relationship_t; using common::model::template_parameter; @@ -40,27 +40,42 @@ class translation_unit_visitor; */ class template_builder { public: + using element_factory_t = + std::function( + const clang::NamedDecl *)>; + + using find_instantiation_relationships_t = std::function; + + using on_argument_base_found_t = + std::function; /** * @brief Constructor. * * @param visitor Reference to class diagram translation_unit_visitor */ - template_builder( - clanguml::class_diagram::visitor::translation_unit_visitor &visitor); + template_builder(clanguml::common::model::diagram &diagram_, + const clanguml::config::diagram &config_, + clanguml::common::visitor::translation_unit_visitor &visitor, + element_factory_t element_factory, + find_instantiation_relationships_t find_instantiation_relationships = + {}, + on_argument_base_found_t on_argument_base_found = {}); /** * @brief Get reference to the current diagram model * * @return Reference to the current diagram model */ - class_diagram::model::diagram &diagram(); + common::model::diagram &diagram(); /** * @brief Get reference to the current diagram configuration * * @return Reference to the current diagram configuration */ - const config::class_diagram &config() const; + const config::diagram &config() const; /** * @brief Get diagram relative namespace @@ -94,10 +109,11 @@ public: * @param parent Optional class in which this template is contained * @return Created template class model */ - std::unique_ptr build( + void build( + clanguml::common::model::template_element &template_instantiation, const clang::NamedDecl *cls, const clang::TemplateSpecializationType &template_type_decl, - std::optional parent = {}); + std::optional parent = {}); /** * @brief Build template class from class template specialization decl @@ -106,10 +122,10 @@ public: * @param parent Optional class in which this template is contained * @return Created template class model */ - std::unique_ptr - build_from_class_template_specialization( + void build_from_class_template_specialization( + clanguml::common::model::template_element &template_instantiation, const clang::ClassTemplateSpecializationDecl &template_specialization, - std::optional parent = {}); + std::optional parent = {}); /** * @brief Add base classes to the template class, if any. @@ -125,7 +141,7 @@ public: * @param ct Template parameter model * @return True, if any base classes were added */ - bool add_base_classes(clanguml::class_diagram::model::class_ &tinst, + bool add_base_classes(clanguml::common::model::template_element &tinst, std::deque> &template_base_params, int arg_index, bool variadic_params, const clanguml::common::model::template_parameter &ct); @@ -141,11 +157,11 @@ public: * @param template_decl Base template declaration */ void process_template_arguments( - std::optional &parent, + std::optional &parent, const clang::NamedDecl *cls, std::deque> &template_base_params, const clang::ArrayRef &template_args, - model::class_ &template_instantiation, + clanguml::common::model::template_element &template_instantiation, const clang::TemplateDecl *template_decl); /** @@ -161,8 +177,9 @@ public: * variadic parameters) */ void argument_process_dispatch( - std::optional &parent, - const clang::NamedDecl *cls, class_ &template_instantiation, + std::optional &parent, + const clang::NamedDecl *cls, + clanguml::common::model::template_element &template_instantiation, const clang::TemplateDecl *template_decl, const clang::TemplateArgument &arg, size_t argument_index, std::vector &argument); @@ -226,8 +243,9 @@ public: * @return Return template argument model */ std::vector process_pack_argument( - std::optional &parent, - const clang::NamedDecl *cls, class_ &template_instantiation, + std::optional &parent, + const clang::NamedDecl *cls, + clanguml::common::model::template_element &template_instantiation, const clang::TemplateDecl *base_template_decl, const clang::TemplateArgument &arg, size_t argument_index, std::vector &argument); @@ -241,10 +259,11 @@ public: * @return Return template argument model */ template_parameter process_type_argument( - std::optional &parent, + std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *base_template_decl, clang::QualType type, - model::class_ &template_instantiation, size_t argument_index); + clanguml::common::model::template_element &template_instantiation, + size_t argument_index); /** * @brief Process `clang::TemplateArgument::Template` @@ -282,9 +301,10 @@ public: * @return Function template argument if succeeds, or std::nullopt */ std::optional try_as_function_prototype( - std::optional &parent, + std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, - clang::QualType &type, class_ &template_instantiation, + clang::QualType &type, + clanguml::common::model::template_element &template_instantiation, size_t argument_index); /** @@ -299,9 +319,10 @@ public: * @return Array template argument if succeeds, or std::nullopt */ std::optional try_as_array( - std::optional &parent, + std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, - clang::QualType &type, class_ &template_instantiation, + clang::QualType &type, + clanguml::common::model::template_element &template_instantiation, size_t argument_index); /** @@ -317,9 +338,10 @@ public: * or std::nullopt */ std::optional try_as_template_specialization_type( - std::optional &parent, + std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, - clang::QualType &type, class_ &template_instantiation, + clang::QualType &type, + clanguml::common::model::template_element &template_instantiation, size_t argument_index); /** @@ -357,9 +379,10 @@ public: * @return Record type template argument if succeeds, or std::nullopt */ std::optional try_as_record_type( - std::optional &parent, + std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, - clang::QualType &type, class_ &template_instantiation, + clang::QualType &type, + clanguml::common::model::template_element &template_instantiation, size_t argument_index); /** @@ -373,9 +396,10 @@ public: * @return Enum type template argument if succeeds, or std::nullopt */ std::optional try_as_enum_type( - std::optional &parent, + std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, - clang::QualType &type, class_ &template_instantiation); + clang::QualType &type, + clanguml::common::model::template_element &template_instantiation); /** * @brief Try to process template type argument as builtin type @@ -386,7 +410,7 @@ public: * @return Builtin type template argument if succeeds, or std::nullopt */ std::optional try_as_builtin_type( - std::optional &parent, + std::optional &parent, clang::QualType &type, const clang::TemplateDecl *template_decl); /** @@ -402,9 +426,10 @@ public: * or std::nullopt */ std::optional try_as_member_pointer( - std::optional &parent, + std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, - clang::QualType &type, class_ &template_instantiation, + clang::QualType &type, + clanguml::common::model::template_element &template_instantiation, size_t argument_index); /** @@ -419,9 +444,10 @@ public: * @return `decltype()` type template argument if succeeds, or std::nullopt */ std::optional try_as_decl_type( - std::optional &parent, + std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, - clang::QualType &type, class_ &template_instantiation, + clang::QualType &type, + clanguml::common::model::template_element &template_instantiation, size_t argument_index); /** @@ -436,9 +462,10 @@ public: * @return Typedef type template argument if succeeds, or std::nullopt */ std::optional try_as_typedef_type( - std::optional &parent, + std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, - clang::QualType &type, class_ &template_instantiation, + clang::QualType &type, + clanguml::common::model::template_element &template_instantiation, size_t argument_index); /** @@ -471,8 +498,11 @@ public: * @return True, if any relationships were found */ bool find_relationships_in_unexposed_template_params( - const template_parameter &ct, - class_diagram::visitor::found_relationships_t &relationships); + const template_parameter &ct, found_relationships_t &relationships); + + void find_instantiation_relationships( + common::model::template_element &template_instantiation, + common::id_t id, const std::string &qualified_name) const; /** * @brief Get reference to Clang AST to clang-uml id mapper @@ -490,16 +520,24 @@ public: private: // Reference to the output diagram model - clanguml::class_diagram::model::diagram &diagram_; + clanguml::common::model::diagram &diagram_; - // Reference to class diagram config - const clanguml::config::class_diagram &config_; + // Reference to diagram config + const clanguml::config::diagram &config_; common::visitor::ast_id_mapper &id_mapper_; clang::SourceManager &source_manager_; - clanguml::class_diagram::visitor::translation_unit_visitor &visitor_; + clanguml::common::visitor::translation_unit_visitor &visitor_; + + element_factory_t element_factory_; + + find_instantiation_relationships_t find_instantiation_relationships_; + + // Callback to call when a template instantiation has arguments which are + // base classes, i.e. when template class inherits from template params + on_argument_base_found_t on_argument_base_found_; }; } // namespace clanguml::class_diagram::visitor \ No newline at end of file diff --git a/src/common/visitor/translation_unit_visitor.h b/src/common/visitor/translation_unit_visitor.h index 6dfbc762..71408ed5 100644 --- a/src/common/visitor/translation_unit_visitor.h +++ b/src/common/visitor/translation_unit_visitor.h @@ -18,8 +18,9 @@ #pragma once #include "comment/comment_visitor.h" -#include "common/model/element.h" #include "common/model/source_location.h" +#include "common/model/template_element.h" +#include "common/visitor/ast_id_mapper.h" #include "config/config.h" #include @@ -65,6 +66,13 @@ public: */ const std::filesystem::path &tu_path() const; + /** + * @brief Get reference to Clang AST to clang-uml id mapper + * + * @return Reference to Clang AST to clang-uml id mapper + */ + common::visitor::ast_id_mapper &id_mapper() const { return id_mapper_; } + /** * @brief Get clang::SourceManager * @return Reference to @ref clang::SourceManager used by this translation @@ -105,6 +113,11 @@ public: void set_owning_module( const clang::Decl &decl, clanguml::common::model::element &element); + virtual void add_diagram_element( + std::unique_ptr element) + { + } + protected: /** * @brief Process comment directives in comment attached to a declaration @@ -136,5 +149,7 @@ private: std::filesystem::path translation_unit_path_; std::set processed_comments_; + + mutable common::visitor::ast_id_mapper id_mapper_; }; } // namespace clanguml::common::visitor diff --git a/src/sequence_diagram/model/diagram.h b/src/sequence_diagram/model/diagram.h index 35f0c465..e075a04f 100644 --- a/src/sequence_diagram/model/diagram.h +++ b/src/sequence_diagram/model/diagram.h @@ -83,7 +83,7 @@ public: } return common::optional_ref( - static_cast(participants_.at(id).get())); + dynamic_cast(participants_.at(id).get())); } /** diff --git a/src/sequence_diagram/model/participant.h b/src/sequence_diagram/model/participant.h index ff86fa2e..950a6f26 100644 --- a/src/sequence_diagram/model/participant.h +++ b/src/sequence_diagram/model/participant.h @@ -18,6 +18,7 @@ #pragma once #include "common/model/element.h" +#include "common/model/template_element.h" #include "common/model/template_parameter.h" #include "common/model/template_trait.h" @@ -31,9 +32,9 @@ using clanguml::common::model::template_trait; /** * @brief Base class for various types of sequence diagram participants */ -struct participant : public common::model::element, +struct participant : public common::model::template_element, public common::model::stylable_element { - using common::model::element::element; + using common::model::template_element::template_element; /** * @brief Enum representing stereotype of a participant @@ -74,7 +75,7 @@ struct participant : public common::model::element, /** * @brief Sequence diagram participant representing a class. */ -struct class_ : public participant, public template_trait { +struct class_ : public participant { public: class_(const common::model::namespace_ &using_namespace); @@ -477,7 +478,7 @@ private: /** * @brief Participant model representing a function template. */ -struct function_template : public function, public template_trait { +struct function_template : public function { function_template(const common::model::namespace_ &using_namespace); function_template(const function_template &) = delete; diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index 5b02b279..13b0f268 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -20,6 +20,7 @@ #include "common/clang_utils.h" #include "common/model/namespace.h" +#include "sequence_diagram/model/participant.h" namespace clanguml::sequence_diagram::visitor { diff --git a/src/util/util.h b/src/util/util.h index 391f5b8c..c56c2ceb 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -160,6 +160,17 @@ std::string get_git_toplevel_dir(); */ std::string get_os_name(); +template +std::unique_ptr unique_pointer_cast(std::unique_ptr &&p) noexcept +{ + if (T *const converted = dynamic_cast(p.get())) { + p.release(); + return std::unique_ptr{converted}; + } + + return {}; +} + /** * @brief Split a string using delimiter * diff --git a/tests/t00044/t00044.cc b/tests/t00044/t00044.cc index 2d71d9bd..0a23aa02 100644 --- a/tests/t00044/t00044.cc +++ b/tests/t00044/t00044.cc @@ -3,7 +3,7 @@ namespace clanguml::t00044 { template class sink; -template class signal_handler; +template struct signal_handler; template class sink> { @@ -22,7 +22,7 @@ private: }; template -class signal_handler { }; +struct signal_handler { }; template sink(signal_handler &) diff --git a/tests/t00044/test_case.h b/tests/t00044/test_case.h index 55610f86..3f04d11d 100644 --- a/tests/t00044/test_case.h +++ b/tests/t00044/test_case.h @@ -74,9 +74,10 @@ TEST_CASE("t00044", "[test-case][class]") REQUIRE(IsClassTemplate(j, "sink")); REQUIRE(IsClassTemplate(j, "signal_handler")); REQUIRE(IsClassTemplate(j, "signal_handler")); + REQUIRE(IsStruct(j, "signal_handler")); REQUIRE(IsClassTemplate(j, "signal_handler")); REQUIRE(IsClassTemplate(j, "sink>")); - REQUIRE(IsClass(j, "R")); + REQUIRE(IsStruct(j, "R")); save_json(config.output_directory(), diagram->name + ".json", j); } diff --git a/tests/test_cases.h b/tests/test_cases.h index c6662815..26883024 100644 --- a/tests/test_cases.h +++ b/tests/test_cases.h @@ -1273,6 +1273,12 @@ bool IsClass(const nlohmann::json &j, const std::string &name) return e && e->at("type") == "class"; } +bool IsStruct(const nlohmann::json &j, const std::string &name) +{ + auto e = get_element(j, expand_name(j, name)); + return e && e->at("type") == "class" && e->at("is_struct"); +} + bool InPublicModule(const nlohmann::json &j, const std::string &element, const std::string &module) {