From a8bab3931e3b17bbbc7fb0a7c8802e56864288ac Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Thu, 25 Mar 2021 22:30:21 +0100 Subject: [PATCH] Fixed basic template instantiation relationships --- src/cx/util.cc | 12 +++ src/cx/util.h | 3 + src/puml/class_diagram_generator.h | 2 +- src/uml/class_diagram_model.cc | 30 +++++++ src/uml/class_diagram_model.h | 4 +- src/uml/class_diagram_visitor.cc | 131 +++++++++++++++++++++++++---- src/uml/class_diagram_visitor.h | 16 +++- tests/t00009/test_case.h | 4 +- tests/t00010/test_case.h | 2 +- 9 files changed, 179 insertions(+), 25 deletions(-) diff --git a/src/cx/util.cc b/src/cx/util.cc index 036f9132..564debce 100644 --- a/src/cx/util.cc +++ b/src/cx/util.cc @@ -91,6 +91,18 @@ std::string fully_prefixed(const cppast::cpp_entity &e) return fmt::format("{}", fmt::join(res.rbegin(), res.rend(), "::")); } + +const cppast::cpp_type &unreferenced(const cppast::cpp_type &t) +{ + if (t.kind() == cppast::cpp_type_kind::pointer_t) + return unreferenced( + static_cast(t).pointee()); + else if (t.kind() == cppast::cpp_type_kind::reference_t) + return unreferenced( + static_cast(t).referee()); + + return t; +} } // namespace util } // namespace cx } // namespace clanguml diff --git a/src/cx/util.h b/src/cx/util.h index 8b3ee165..2602adfb 100644 --- a/src/cx/util.h +++ b/src/cx/util.h @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -43,6 +44,8 @@ std::string full_name(const cppast::cpp_entity &e); std::string fully_prefixed(const cppast::cpp_entity &e); +const cppast::cpp_type& unreferenced(const cppast::cpp_type &t); + } // namespace util } // namespace cx } // namespace clanguml diff --git a/src/puml/class_diagram_generator.h b/src/puml/class_diagram_generator.h index 92dc0f5a..f5976fba 100644 --- a/src/puml/class_diagram_generator.h +++ b/src/puml/class_diagram_generator.h @@ -318,7 +318,7 @@ clanguml::model::class_diagram::diagram generate( type_safe::ref(idx)}; // Process all matching translation units - clanguml::visitor::class_diagram::tu_visitor ctx(d, diagram); + clanguml::visitor::class_diagram::tu_visitor ctx(idx, d, diagram); cppast::parse_files(parser, translation_units, db); for (auto &file : parser.files()) ctx(file); diff --git a/src/uml/class_diagram_model.cc b/src/uml/class_diagram_model.cc index 97b48ae8..0d7ea761 100644 --- a/src/uml/class_diagram_model.cc +++ b/src/uml/class_diagram_model.cc @@ -22,6 +22,36 @@ namespace clanguml { namespace model { namespace class_diagram { std::atomic_uint64_t element::m_nextId = 1; + +std::string to_string(relationship_t r) +{ + switch (r) { + case relationship_t::kNone: + return "none"; + case relationship_t::kExtension: + return "extension"; + case relationship_t::kComposition: + return "composition"; + case relationship_t::kAggregation: + return "aggregation"; + case relationship_t::kContainment: + return "containment"; + case relationship_t::kOwnership: + return "ownership"; + case relationship_t::kAssociation: + return "association"; + case relationship_t::kInstantiation: + return "instantiation"; + case relationship_t::kFriendship: + return "frendship"; + case relationship_t::kDependency: + return "dependency"; + default: + return "invalid"; + } +} + + } } } diff --git a/src/uml/class_diagram_model.h b/src/uml/class_diagram_model.h index fca6f73d..80138cf1 100644 --- a/src/uml/class_diagram_model.h +++ b/src/uml/class_diagram_model.h @@ -49,6 +49,8 @@ enum class relationship_t { kDependency }; +std::string to_string(relationship_t r); + class element { public: element() @@ -226,7 +228,7 @@ struct diagram { void add_class(class_ &&c) { - spdlog::debug("ADDING CLASS: {}, {}", c.name, c.usr); + spdlog::debug("Adding class: {}, {}", c.name, c.usr); auto it = std::find(classes.begin(), classes.end(), c); if (it == classes.end()) classes.emplace_back(std::move(c)); diff --git a/src/uml/class_diagram_visitor.cc b/src/uml/class_diagram_visitor.cc index 499a3330..ca7be2ce 100644 --- a/src/uml/class_diagram_visitor.cc +++ b/src/uml/class_diagram_visitor.cc @@ -18,6 +18,7 @@ #include "class_diagram_visitor.h" #include +#include #include #include #include @@ -123,7 +124,6 @@ void tu_visitor::process_enum_declaration(const cppast::cpp_enum &enm) void tu_visitor::process_class_declaration(const cppast::cpp_class &cls) { class_ c; - c.usr = cx::util::full_name(cls); c.is_struct = cls.class_kind() == cppast::cpp_class_kind::struct_t; c.name = cx::util::full_name(cls); @@ -230,6 +230,8 @@ void tu_visitor::process_class_declaration(const cppast::cpp_class &cls) break; } } + cls.set_user_data(strdup(c.full_name(ctx.config.using_namespace).c_str())); + c.usr = c.full_name(ctx.config.using_namespace); ctx.d.add_class(std::move(c)); } @@ -243,6 +245,52 @@ void tu_visitor::process_field(const cppast::cpp_member_variable &mv, class_ &c, m.scope = detail::cpp_access_specifier_to_scope(as); m.is_static = false; + const auto &tr = cx::util::unreferenced(mv.type()); + if (tr.kind() == cppast::cpp_type_kind::template_instantiation_t) { + const auto &template_instantiation_type = + static_cast(tr); + if (template_instantiation_type.primary_template() + .get(ctx.entity_index) + .size()) { + // Here we need the name of the primary template with full namespace + // prefix to apply config inclusion filters + auto primary_template_name = cx::util::full_name( + template_instantiation_type.primary_template() + .get(ctx.entity_index)[0] + .get()); + + spdlog::debug( + "MAYBE BUILDING INSTANTIATION FOR: {}", primary_template_name); + + if (ctx.config.should_include(primary_template_name)) { + class_ tinst = build_template_instantiation( + mv, template_instantiation_type); + + class_relationship r; + r.destination = tinst.base_template_usr; + r.type = relationship_t::kInstantiation; + r.label = ""; + tinst.add_relationship(std::move(r)); + + class_relationship rr; + rr.destination = tinst.usr; + if (mv.type().kind() == cppast::cpp_type_kind::pointer_t || + mv.type().kind() == cppast::cpp_type_kind::reference_t) + rr.type = relationship_t::kAssociation; + else + rr.type = relationship_t::kComposition; + rr.label = mv.name(); + spdlog::debug( + "Adding field instantiation relationship {} {} {} : {}", + rr.destination, model::class_diagram::to_string(rr.type), + c.usr, rr.label); + c.add_relationship(std::move(rr)); + + ctx.d.add_class(std::move(tinst)); + } + } + } + if (mv.type().kind() != cppast::cpp_type_kind::builtin_t) { std::vector> relationships; @@ -254,9 +302,12 @@ void tu_visitor::process_field(const cppast::cpp_member_variable &mv, class_ &c, r.destination = type; r.type = relationship_type; r.label = m.name; - c.relationships.emplace_back(std::move(r)); - spdlog::debug("Added relationship to: {}", r.destination); + spdlog::debug("Adding field relationship {} {} {} : {}", + r.destination, model::class_diagram::to_string(r.type), + c.usr, r.label); + + c.relationships.emplace_back(std::move(r)); } } } @@ -419,8 +470,10 @@ void tu_visitor::find_relationships(const cppast::cpp_type &t_, std::vector> &relationships, relationship_t relationship_hint) { + spdlog::debug("Finding relationships for type {}", cppast::to_string(t_)); + relationship_t relationship_type = relationship_hint; - const auto &t = cppast::remove_cv(t_); + const auto &t = cppast::remove_cv(cx::util::unreferenced(t_)); if (t.kind() == cppast::cpp_type_kind::array_t) { auto &a = static_cast(t); @@ -460,7 +513,7 @@ void tu_visitor::find_relationships(const cppast::cpp_type &t_, } else if (name.find("std::vector") == 0) { find_relationships(args[0u].type().value(), relationships, - relationship_t::kAggregation); + relationship_t::kComposition); } else { for (const auto &arg : args) { @@ -469,26 +522,72 @@ void tu_visitor::find_relationships(const cppast::cpp_type &t_, } } } - else if (t.kind() == cppast::cpp_type_kind::user_defined_t) { - if (relationship_type != relationship_t::kNone) - relationships.emplace_back(cppast::to_string(t), relationship_hint); - else - relationships.emplace_back( - cppast::to_string(t), relationship_t::kComposition); - } - else if (t.kind() == cppast::cpp_type_kind::pointer_t) { - auto &p = static_cast(t); + else if (t_.kind() == cppast::cpp_type_kind::pointer_t) { + auto &p = static_cast(t_); + spdlog::debug("TEST2"); find_relationships( p.pointee(), relationships, relationship_t::kAssociation); } - else if (t.kind() == cppast::cpp_type_kind::reference_t) { - auto &r = static_cast(t); + else if (t_.kind() == cppast::cpp_type_kind::reference_t) { + spdlog::debug("TEST3"); + auto &r = static_cast(t_); auto rt = relationship_t::kAssociation; if (r.reference_kind() == cppast::cpp_reference::cpp_ref_rvalue) { rt = relationship_t::kComposition; } find_relationships(r.referee(), relationships, rt); } + else if (cppast::remove_cv(t_).kind() == + cppast::cpp_type_kind::user_defined_t) { + if (ctx.config.should_include(cppast::to_string(t_.canonical()))) + if (relationship_type != relationship_t::kNone) + relationships.emplace_back( + cppast::to_string(cppast::remove_cv(t_)), relationship_type); + else + relationships.emplace_back( + cppast::to_string(cppast::remove_cv(t_)), relationship_t::kComposition); + } +} + +class_ tu_visitor::build_template_instantiation(const cppast::cpp_entity &e, + const cppast::cpp_template_instantiation_type &t) +{ + spdlog::debug("Found template instantiation: {} ..|> {}", + cppast::to_string(t.canonical()), t.primary_template().name()); + + class_ tinst; + const auto &primary_template_ref = + static_cast( + t.primary_template().get(ctx.entity_index)[0].get()) + .class_(); + tinst.name = primary_template_ref.name(); + if (primary_template_ref.user_data()) + tinst.base_template_usr = + static_cast(primary_template_ref.user_data()); + + tinst.is_template_instantiation = true; + + for (const auto &targ : t.arguments().value()) { + class_template ct; + if (targ.type()) { + ct.type = cppast::to_string(targ.type().value()); + } + else if (targ.expression()) { + const auto &exp = targ.expression().value(); + if (exp.kind() == cppast::cpp_expression_kind::literal_t) + ct.type = + static_cast(exp) + .value(); + } + + spdlog::debug("Adding template argument '{}'", ct.type); + + tinst.templates.emplace_back(std::move(ct)); + } + + tinst.usr = tinst.full_name(ctx.config.using_namespace); + + return tinst; } // diff --git a/src/uml/class_diagram_visitor.h b/src/uml/class_diagram_visitor.h index f5488518..ccbe95f9 100644 --- a/src/uml/class_diagram_visitor.h +++ b/src/uml/class_diagram_visitor.h @@ -38,14 +38,17 @@ namespace visitor { namespace class_diagram { struct tu_context { - tu_context(clanguml::model::class_diagram::diagram &d_, + tu_context(cppast::cpp_entity_index &idx, + clanguml::model::class_diagram::diagram &d_, const clanguml::config::class_diagram &config_) - : d{d_} + : entity_index{idx} + , d{d_} , config{config_} { } std::vector namespace_; + cppast::cpp_entity_index &entity_index; clanguml::model::class_diagram::diagram &d; const clanguml::config::class_diagram &config; }; @@ -65,9 +68,10 @@ template struct element_visitor_context { class tu_visitor { public: - tu_visitor(clanguml::model::class_diagram::diagram &d_, + tu_visitor(cppast::cpp_entity_index &idx_, + clanguml::model::class_diagram::diagram &d_, const clanguml::config::class_diagram &config_) - : ctx{d_, config_} + : ctx{idx_, d_, config_} { } @@ -123,6 +127,10 @@ public: clanguml::model::class_diagram::class_ &parent); private: + clanguml::model::class_diagram::class_ build_template_instantiation( + const cppast::cpp_entity &e, + const cppast::cpp_template_instantiation_type &t); + tu_context ctx; }; diff --git a/tests/t00009/test_case.h b/tests/t00009/test_case.h index dde44565..2235b9f1 100644 --- a/tests/t00009/test_case.h +++ b/tests/t00009/test_case.h @@ -47,9 +47,9 @@ TEST_CASE("t00009", "[test-case][class]") REQUIRE_THAT(puml, IsField(Public("T value"))); REQUIRE_THAT(puml, IsField(Public("A aint"))); - REQUIRE_THAT(puml, IsField(Public("A * astring"))); + REQUIRE_THAT(puml, IsField(Public("A* astring"))); REQUIRE_THAT( - puml, IsField(Public("A> & avector"))); + puml, IsField(Public("A>& avector"))); REQUIRE_THAT(puml, IsInstantiation(_A("A"), _A("A"))); REQUIRE_THAT(puml, IsInstantiation(_A("A"), _A("A"))); diff --git a/tests/t00010/test_case.h b/tests/t00010/test_case.h index 2794fcf0..8cc36da7 100644 --- a/tests/t00010/test_case.h +++ b/tests/t00010/test_case.h @@ -45,7 +45,7 @@ TEST_CASE("t00010", "[test-case][class]") REQUIRE_THAT(puml, IsClassTemplate("A", "T, P")); REQUIRE_THAT(puml, IsClassTemplate("B", "T")); - REQUIRE_THAT(puml, IsField(Public("A astring"))); + REQUIRE_THAT(puml, IsField(Public("A astring"))); REQUIRE_THAT(puml, IsField(Public("B aintstring"))); REQUIRE_THAT(puml, IsInstantiation(_A("A"), _A("A")));