From 4aedc6e3304ed1162a3e7d14523f19a12dcec5a1 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Mon, 24 Jan 2022 19:59:59 +0100 Subject: [PATCH] Implemented package dependency test case --- .../visitor/translation_unit_visitor.cc | 260 ++++++++++++++++-- .../visitor/translation_unit_visitor.h | 10 +- tests/t30002/t30002.cc | 91 +++++- tests/t30002/test_case.h | 11 +- 4 files changed, 331 insertions(+), 41 deletions(-) diff --git a/src/package_diagram/visitor/translation_unit_visitor.cc b/src/package_diagram/visitor/translation_unit_visitor.cc index 18d43cd5..ffda2392 100644 --- a/src/package_diagram/visitor/translation_unit_visitor.cc +++ b/src/package_diagram/visitor/translation_unit_visitor.cc @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -156,9 +155,27 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file) } } - if (ctx.config().should_include( - cx::util::fully_prefixed(ctx.get_namespace(), cls))) - process_class_declaration(cls); + process_class_declaration(cls); + } + else if (e.kind() == cppast::cpp_entity_kind::function_t) { + LOG_DBG("========== Visiting '{}' - {}", + cx::util::full_name(ctx.get_namespace(), e), + cppast::to_string(e.kind())); + + auto &f = static_cast(e); + + process_function(f); + } + else if (e.kind() == cppast::cpp_entity_kind::function_template_t) { + LOG_DBG("========== Visiting '{}' - {}", + cx::util::full_name(ctx.get_namespace(), e), + cppast::to_string(e.kind())); + + auto &f = static_cast( + static_cast(e) + .function()); + + process_function(f); } else if (e.kind() == cppast::cpp_entity_kind::type_alias_t) { LOG_DBG("========== Visiting '{}' - {}", @@ -198,6 +215,9 @@ void translation_unit_visitor::process_class_declaration( cppast::cpp_access_specifier_kind last_access_specifier = cppast::cpp_access_specifier_kind::cpp_private; + std::vector> relationships; + + // Process class elements for (auto &child : cls) { if (child.kind() == cppast::cpp_entity_kind::access_specifier_t) { auto &as = static_cast(child); @@ -205,46 +225,232 @@ void translation_unit_visitor::process_class_declaration( } else if (child.kind() == cppast::cpp_entity_kind::member_variable_t) { auto &mv = static_cast(child); - process_field(mv, current_package, last_access_specifier); + find_relationships( + mv.type(), relationships, relationship_t::kDependency); + } + else if (child.kind() == cppast::cpp_entity_kind::variable_t) { + auto &mv = static_cast(child); + find_relationships( + mv.type(), relationships, relationship_t::kDependency); + } + else if (child.kind() == cppast::cpp_entity_kind::member_function_t) { + auto &mf = static_cast(child); + for (const auto ¶m : mf.parameters()) + find_relationships( + param.type(), relationships, relationship_t::kDependency); + + find_relationships( + mf.return_type(), relationships, relationship_t::kDependency); + } + else if (child.kind() == cppast::cpp_entity_kind::function_t) { + auto &mf = static_cast(child); + for (const auto ¶m : mf.parameters()) + find_relationships( + param.type(), relationships, relationship_t::kDependency); + } + else if (child.kind() == cppast::cpp_entity_kind::function_template_t) { + auto &tm = static_cast(child) + .function(); + for (const auto ¶m : tm.parameters()) + find_relationships( + param.type(), relationships, relationship_t::kDependency); + + if (tm.kind() == cppast::cpp_entity_kind::member_function_t) + find_relationships( + static_cast(tm) + .return_type(), + relationships, relationship_t::kDependency); + } + else if (child.kind() == cppast::cpp_entity_kind::constructor_t) { + auto &mc = static_cast(child); + for (const auto ¶m : mc.parameters()) + find_relationships( + param.type(), relationships, relationship_t::kDependency); } else { LOG_DBG("Found some other class child: {} ({})", child.name(), cppast::to_string(child.kind())); } } -} -void translation_unit_visitor::process_field( - const cppast::cpp_member_variable &mv, - type_safe::optional_ref p, - cppast::cpp_access_specifier_kind as) -{ - auto &type = cx::util::unreferenced(cppast::remove_cv(mv.type())); - auto type_ns = - util::split(cx::util::full_name(type, ctx.entity_index(), false), "::"); - type_ns.pop_back(); + // Process class bases + for (auto &base : cls.bases()) { + find_relationships( + base.type(), relationships, relationship_t::kDependency); - if (type.kind() == cppast::cpp_type_kind::user_defined_t) { - LOG_DBG("Processing user defined type field {} {}", - cppast::to_string(type), mv.name()); + clanguml::cx::util::fully_prefixed(ctx.get_namespace(), base); + } + + for (const auto &dependency : relationships) { + auto type_ns = util::split(std::get<0>(dependency), "::"); + type_ns.pop_back(); + + relationship r{relationship_t::kDependency, + fmt::format("{}", fmt::join(type_ns, "::"))}; if (!starts_with(ctx.get_namespace(), type_ns) && !starts_with(type_ns, ctx.get_namespace())) { relationship r{relationship_t::kDependency, fmt::format("{}", fmt::join(type_ns, "::"))}; - p.value().add_relationship(std::move(r)); + current_package.value().add_relationship(std::move(r)); } } - else if (type.kind() == cppast::cpp_type_kind::template_instantiation_t) { - // template_instantiation_added_as_aggregation = - // process_field_with_template_instantiation( - // mv, resolve_alias(type), c, m, as); - LOG_DBG("Processing template instantiation type {} {}", - cppast::to_string(type), mv.name()); +} + +void translation_unit_visitor::process_function(const cppast::cpp_function &f) +{ + std::vector> relationships; + auto current_package = ctx.get_current_package(); + + if (!current_package) + return; + + for (const auto ¶m : f.parameters()) + find_relationships( + param.type(), relationships, relationship_t::kDependency); + + find_relationships( + f.return_type(), relationships, relationship_t::kDependency); + + for (const auto &dependency : relationships) { + auto type_ns = util::split(std::get<0>(dependency), "::"); + type_ns.pop_back(); + + relationship r{relationship_t::kDependency, + fmt::format("{}", fmt::join(type_ns, "::"))}; + + if (!starts_with(ctx.get_namespace(), type_ns) && + !starts_with(type_ns, ctx.get_namespace())) { + relationship r{relationship_t::kDependency, + fmt::format("{}", fmt::join(type_ns, "::"))}; + current_package.value().add_relationship(std::move(r)); + } } - else { - LOG_DBG("Skipping field type: {}", cppast::to_string(type)); +} + +bool translation_unit_visitor::find_relationships(const cppast::cpp_type &t_, + std::vector> + &relationships, + relationship_t relationship_hint) +{ + bool found{false}; + + const auto fn = + cx::util::full_name(cppast::remove_cv(t_), ctx.entity_index(), false); + + LOG_DBG("Finding relationships for type {}, {}, {}", cppast::to_string(t_), + t_.kind(), fn); + + relationship_t relationship_type = relationship_hint; + const auto &t = cppast::remove_cv(cx::util::unreferenced(t_)); + + if (t.kind() == cppast::cpp_type_kind::array_t) { + auto &a = static_cast(t); + found = find_relationships( + a.value_type(), relationships, relationship_t::kDependency); + return found; } + + auto name = cppast::to_string(t); + + if (t_.kind() == cppast::cpp_type_kind::pointer_t) { + auto &p = static_cast(t_); + auto rt = relationship_t::kAssociation; + if (relationship_hint == relationship_t::kDependency) + rt = relationship_hint; + found = find_relationships(p.pointee(), relationships, rt); + } + else if (t_.kind() == cppast::cpp_type_kind::reference_t) { + auto &r = static_cast(t_); + auto rt = relationship_t::kAssociation; + if (r.reference_kind() == cppast::cpp_reference::cpp_ref_rvalue) { + rt = relationship_t::kAggregation; + } + if (relationship_hint == relationship_t::kDependency) + rt = relationship_hint; + found = find_relationships(r.referee(), relationships, rt); + } + if (cppast::remove_cv(t_).kind() == cppast::cpp_type_kind::user_defined_t) { + LOG_DBG("User defined type: {} | {}", cppast::to_string(t_), + cppast::to_string(t_.canonical())); + + if (relationship_type != relationship_t::kNone) + relationships.emplace_back(cppast::to_string(t), relationship_type); + else + relationships.emplace_back( + cppast::to_string(t), relationship_t::kDependency); + + // Check if t_ has an alias in the alias index + if (ctx.has_type_alias(fn)) { + LOG_DBG("Find relationship in alias of {} | {}", fn, + cppast::to_string(ctx.get_type_alias(fn).get())); + found = find_relationships( + ctx.get_type_alias(fn).get(), relationships, relationship_type); + if (found) + return found; + } + } + else if (t.kind() == cppast::cpp_type_kind::template_instantiation_t) { + auto &tinst = + static_cast(t); + + if (!tinst.arguments_exposed()) { + LOG_DBG("Template instantiation {} has no exposed arguments", name); + + return found; + } + + const auto &args = tinst.arguments().value(); + + // Try to match common containers + // TODO: Refactor to a separate class with configurable + // container list + if (name.find("std::unique_ptr") == 0) { + found = find_relationships(args[0u].type().value(), relationships, + relationship_t::kDependency); + } + else if (name.find("std::shared_ptr") == 0) { + found = find_relationships(args[0u].type().value(), relationships, + relationship_t::kDependency); + } + else if (name.find("std::weak_ptr") == 0) { + found = find_relationships(args[0u].type().value(), relationships, + relationship_t::kDependency); + } + else if (name.find("std::vector") == 0) { + found = find_relationships(args[0u].type().value(), relationships, + relationship_t::kDependency); + } + else if (ctx.config().should_include(fn)) { + LOG_DBG("User defined template instantiation: {} | {}", + cppast::to_string(t_), cppast::to_string(t_.canonical())); + + relationships.emplace_back( + cppast::to_string(t), relationship_t::kDependency); + + // Check if t_ has an alias in the alias index + if (ctx.has_type_alias(fn)) { + LOG_DBG("Find relationship in alias of {} | {}", fn, + cppast::to_string(ctx.get_type_alias(fn).get())); + found = find_relationships(ctx.get_type_alias(fn).get(), + relationships, relationship_type); + if (found) + return found; + } + + return found; + } + else { + for (const auto &arg : args) { + if (arg.type()) { + found = find_relationships( + arg.type().value(), relationships, relationship_type); + } + } + } + } + + return found; } const cppast::cpp_type &translation_unit_visitor::resolve_alias( diff --git a/src/package_diagram/visitor/translation_unit_visitor.h b/src/package_diagram/visitor/translation_unit_visitor.h index c99b6c4c..ca237e9c 100644 --- a/src/package_diagram/visitor/translation_unit_visitor.h +++ b/src/package_diagram/visitor/translation_unit_visitor.h @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -54,9 +55,12 @@ public: type_safe::optional_ref tspec = nullptr); - void process_field(const cppast::cpp_member_variable &mv, - type_safe::optional_ref p, - cppast::cpp_access_specifier_kind as); + void process_function(const cppast::cpp_function &f); + + bool find_relationships(const cppast::cpp_type &t_, + std::vector> + &relationships, + common::model::relationship_t relationship_hint); private: /** diff --git a/tests/t30002/t30002.cc b/tests/t30002/t30002.cc index 8069ff0e..690ec7c9 100644 --- a/tests/t30002/t30002.cc +++ b/tests/t30002/t30002.cc @@ -1,15 +1,98 @@ +#include #include +#include namespace clanguml { namespace t30002 { -namespace A::AA::AAA { +namespace A::AA { +namespace A1 { struct CA { }; } -namespace B::BB::BBB { -struct CBA { - A::AA::AAA::CA *ca_; +namespace A2 { +struct CB { }; } +namespace A3 { +struct CC { +}; +} +namespace A4 { +struct CD { +}; +} +namespace A5 { +struct CE { +}; +} +namespace A6 { +struct CF { +}; +} +namespace A7 { +struct CG { +}; +} +namespace A8 { +struct CH { +}; +} +namespace A9 { +struct CI { +}; +} +namespace A10 { +struct CJ { +}; +} +namespace A11 { +struct CK { +}; +} +namespace A12 { +struct CL { +}; +} +namespace A13 { +struct CM { +}; +} +} +namespace B::BB::BBB { +struct CBA : public A::AA::A6::CF { + A::AA::A1::CA *ca_; + A::AA::A2::CB cb_; + std::shared_ptr cc_; + std::map> cd_; + + void ce(const std::vector /*ce_*/) { } + + std::shared_ptr cg() { return {}; } + + template + void ch(std::map> & /*ch_*/) + { + } + + template std::map> ci() + { + return {}; + } +}; + +void cj(std::unique_ptr /*cj_*/) { } + +std::unique_ptr ck() { return {}; } + +template +void cl(std::map> & /*ch_*/) +{ +} + +template std::map> cm() +{ + return {}; +} +} } // namespace t30002 } // namespace clanguml diff --git a/tests/t30002/test_case.h b/tests/t30002/test_case.h index 067c5742..734e7701 100644 --- a/tests/t30002/test_case.h +++ b/tests/t30002/test_case.h @@ -46,13 +46,10 @@ TEST_CASE("t30002", "[test-case][package]") REQUIRE_THAT(puml, StartsWith("@startuml")); REQUIRE_THAT(puml, EndsWith("@enduml\n")); - REQUIRE_THAT(puml, Contains("component [A]")); - REQUIRE_THAT(puml, Contains("component [AA]")); - REQUIRE_THAT(puml, Contains("component [AAA]")); - - REQUIRE_THAT(puml, Contains("component [B]")); - REQUIRE_THAT(puml, Contains("component [BB]")); - REQUIRE_THAT(puml, Contains("component [BBB]")); + REQUIRE_THAT(puml, Contains("component [A1]")); + REQUIRE_THAT(puml, Contains("component [A2]")); + REQUIRE_THAT(puml, Contains("component [A3]")); + REQUIRE_THAT(puml, Contains("component [A12]")); // REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("AAA")));