From 40dec399956fc2de0f398d669de34c13ff0f4b14 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Sun, 13 Mar 2022 23:33:08 +0100 Subject: [PATCH] Added detection of dependency relationships from unexposed template params --- .../visitor/translation_unit_visitor.cc | 74 +++++++++++++++++-- .../visitor/translation_unit_visitor.h | 16 ++-- src/common/model/namespace.cc | 6 ++ src/common/model/namespace.h | 1 + src/util/util.h | 10 +++ tests/t00038/t00038.cc | 7 +- tests/t00038/test_case.h | 18 ++++- 7 files changed, 114 insertions(+), 18 deletions(-) diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index c0c6f5e3..82cfc8cf 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -178,6 +178,17 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file) process_type_alias_template(at); } + else if (e.kind() == cppast::cpp_entity_kind::using_directive_t) { + + const auto &using_directive = + static_cast(e); + + const auto ns_ref = using_directive.target(); + if (ns_ref.get(ctx.entity_index()).size() > 0) { + auto full_ns = cx::util::full_name(ctx.get_namespace(), + ns_ref.get(ctx.entity_index()).at(0).get()); + } + } }); } @@ -491,10 +502,17 @@ void translation_unit_visitor:: auto ua = tspec.value().unexposed_arguments().as_string(); auto template_params = cx::util::parse_unexposed_template_params(ua); + + found_relationships_t relationships; for (const auto ¶m : template_params) { + find_relationships_in_unexposed_template_params(param, relationships); c.add_template(param); } + for (auto &r : relationships) { + c.add_relationship({std::get<1>(r), std::get<0>(r)}); + } + const auto &primary_template_ref = static_cast( tspec.value().primary_template().get(ctx.entity_index())[0].get()) @@ -1193,7 +1211,8 @@ void translation_unit_visitor::process_friend( } bool translation_unit_visitor::find_relationships(const cppast::cpp_type &t_, - found_relationships_t &relationships, relationship_t relationship_hint) + found_relationships_t &relationships, + relationship_t relationship_hint) const { bool found{false}; @@ -1234,7 +1253,8 @@ bool translation_unit_visitor::find_relationships(const cppast::cpp_type &t_, bool translation_unit_visitor::find_relationships_in_template_instantiation( const cppast::cpp_type &t_, const std::string &fn, - found_relationships_t &relationships, relationship_t relationship_type) + found_relationships_t &relationships, + relationship_t relationship_type) const { const auto &t = cppast::remove_cv(cx::util::unreferenced(t_)); @@ -1321,7 +1341,7 @@ bool translation_unit_visitor::find_relationships_in_template_instantiation( bool translation_unit_visitor::find_relationships_in_user_defined_type( const cppast::cpp_type &t_, found_relationships_t &relationships, const std::string &fn, relationship_t &relationship_type, - const cppast::cpp_type &t) + const cppast::cpp_type &t) const { bool found; LOG_DBG("Finding relationships in user defined type: {} | {}", @@ -1348,7 +1368,7 @@ bool translation_unit_visitor::find_relationships_in_user_defined_type( bool translation_unit_visitor::find_relationships_in_reference( const cppast::cpp_type &t_, found_relationships_t &relationships, - const relationship_t &relationship_hint) + const relationship_t &relationship_hint) const { bool found; auto &r = static_cast(t_); @@ -1364,7 +1384,7 @@ bool translation_unit_visitor::find_relationships_in_reference( bool translation_unit_visitor::find_relationships_in_pointer( const cppast::cpp_type &t_, found_relationships_t &relationships, - const relationship_t &relationship_hint) + const relationship_t &relationship_hint) const { bool found; auto &p = static_cast(t_); @@ -1376,7 +1396,7 @@ bool translation_unit_visitor::find_relationships_in_pointer( } bool translation_unit_visitor::find_relationships_in_array( - found_relationships_t &relationships, const cppast::cpp_type &t) + found_relationships_t &relationships, const cppast::cpp_type &t) const { bool found; auto &a = static_cast(t); @@ -1385,6 +1405,48 @@ bool translation_unit_visitor::find_relationships_in_array( return found; } +bool translation_unit_visitor::find_relationships_in_unexposed_template_params( + const class_template &ct, found_relationships_t &relationships) const +{ + bool found{false}; + LOG_DBG("Finding relationships in user defined type: {}", + ct.to_string(ctx.config().using_namespace())); + + if (!util::contains(ct.type(), "::")) { + // The type name has no namespace - assume it is in the current + // namespace + // TODO: try also all namespaces declared through 'using namespace' + // directive in the current scope + if (ctx.config().should_include(ctx.get_namespace(), ct.type())) { + relationships.emplace_back(ct.type(), relationship_t::kDependency); + found = true; + } + } + else { + // Calculate the complete namespace of the type + auto type_ns = common::model::namespace_{ct.type()}; + auto type_name = type_ns.name(); + type_ns.pop_back(); + + auto current_ns = ctx.get_namespace(); + if (current_ns.ends_with(type_ns)) { + if (ctx.config().should_include(current_ns, type_name)) { + auto full_name = (current_ns | type_name).to_string(); + relationships.emplace_back( + full_name, relationship_t::kDependency); + found = true; + } + } + } + + for (const auto &nested_template_params : ct.template_params_) { + found = find_relationships_in_unexposed_template_params( + nested_template_params, relationships) || + found; + } + return found; +} + std::unique_ptr translation_unit_visitor::build_template_instantiation( const cppast::cpp_template_instantiation_type &t, std::optional parent) diff --git a/src/class_diagram/visitor/translation_unit_visitor.h b/src/class_diagram/visitor/translation_unit_visitor.h index 2cbe8d71..07bcc5e5 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.h +++ b/src/class_diagram/visitor/translation_unit_visitor.h @@ -109,7 +109,7 @@ public: bool find_relationships(const cppast::cpp_type &t, found_relationships_t &relationships, clanguml::common::model::relationship_t relationship_hint = - clanguml::common::model::relationship_t::kNone); + clanguml::common::model::relationship_t::kNone) const; void process_template_type_parameter( const cppast::cpp_template_type_parameter &t, @@ -175,24 +175,28 @@ private: const cppast::cpp_type &type); bool find_relationships_in_array( - found_relationships_t &relationships, const cppast::cpp_type &t); + found_relationships_t &relationships, const cppast::cpp_type &t) const; bool find_relationships_in_pointer(const cppast::cpp_type &t_, found_relationships_t &relationships, - const common::model::relationship_t &relationship_hint); + const common::model::relationship_t &relationship_hint) const; bool find_relationships_in_reference(const cppast::cpp_type &t_, found_relationships_t &relationships, - const common::model::relationship_t &relationship_hint); + const common::model::relationship_t &relationship_hint) const; bool find_relationships_in_user_defined_type(const cppast::cpp_type &t_, found_relationships_t &relationships, const std::string &fn, common::model::relationship_t &relationship_type, - const cppast::cpp_type &t); + const cppast::cpp_type &t) const; bool find_relationships_in_template_instantiation(const cppast::cpp_type &t, const std::string &fn, found_relationships_t &relationships, - common::model::relationship_t relationship_type); + common::model::relationship_t relationship_type) const; + + bool find_relationships_in_unexposed_template_params( + const model::class_template &ct, + found_relationships_t &relationships) const; void build_template_instantiation_primary_template( const cppast::cpp_template_instantiation_type &t, diff --git a/src/common/model/namespace.cc b/src/common/model/namespace.cc index 359bf654..8980eb12 100644 --- a/src/common/model/namespace.cc +++ b/src/common/model/namespace.cc @@ -129,9 +129,15 @@ const std::string &namespace_::operator[](const int index) const bool namespace_::starts_with(const namespace_ &right) const { + return util::starts_with(namespace_path_, right.namespace_path_); } +bool namespace_::ends_with(const namespace_ &right) const +{ + return util::ends_with(namespace_path_, right.namespace_path_); +} + namespace_ namespace_::common_path(const namespace_ &right) const { namespace_ res{}; diff --git a/src/common/model/namespace.h b/src/common/model/namespace.h index d38a5a16..a6fc88a6 100644 --- a/src/common/model/namespace.h +++ b/src/common/model/namespace.h @@ -68,6 +68,7 @@ public: void pop_back(); bool starts_with(const namespace_ &right) const; + bool ends_with(const namespace_ &right) const; namespace_ common_path(const namespace_ &right) const; namespace_ relative_to(const namespace_ &right) const; std::string relative(const std::string &name) const; diff --git a/src/util/util.h b/src/util/util.h index a349aa30..e6243fa6 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -158,6 +158,16 @@ bool starts_with(const std::vector &col, const std::vector &prefix) std::vector(col.begin(), col.begin() + prefix.size()); } +template +bool ends_with(const std::vector &col, const std::vector &suffix) +{ + if (suffix.size() > col.size()) + return false; + + return std::vector(suffix.rbegin(), suffix.rend()) == + std::vector(col.rbegin(), col.rbegin() + suffix.size()); +} + /** * @brief Removes prefix sequence of elements from the beggining of col. * diff --git a/tests/t00038/t00038.cc b/tests/t00038/t00038.cc index 531a44fe..63dec538 100644 --- a/tests/t00038/t00038.cc +++ b/tests/t00038/t00038.cc @@ -22,12 +22,13 @@ struct key_t { template struct map; template <> -struct map> : A { +struct map> : A { }; template <> -struct map< - std::vector>> +struct map>> : B { }; diff --git a/tests/t00038/test_case.h b/tests/t00038/test_case.h index 6f429bbb..102f4982 100644 --- a/tests/t00038/test_case.h +++ b/tests/t00038/test_case.h @@ -45,7 +45,8 @@ TEST_CASE("t00038", "[test-case][class]") "std::integral_constant")); REQUIRE_THAT(puml, IsClassTemplate("map", - "std::vector>")); REQUIRE_THAT(puml, IsClassTemplate("map", @@ -61,17 +62,28 @@ TEST_CASE("t00038", "[test-case][class]") // TODO: Add parsing of unexposed template arguments to infer // additional relationships - /* + REQUIRE_THAT(puml, IsDependency(_A("map>"), _A("property_t"))); + REQUIRE_THAT(puml, + IsDependency(_A("map<" + "std::vector>>"), + _A("property_t"))); + + REQUIRE_THAT(puml, + IsDependency(_A("map>>>"), + _A("property_t"))); + REQUIRE_THAT(puml, IsDependency(_A("map>>>"), _A("key_t"))); - */ save_puml( "./" + config.output_directory() + "/" + diagram->name + ".puml", puml);