From 8ad4c4f5dc4bc15552644b3cbef42884333e3d26 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Fri, 11 Mar 2022 00:28:31 +0100 Subject: [PATCH 01/11] Added parser util function for unexposed template params --- src/class_diagram/model/class_template.h | 2 + src/cx/util.cc | 76 ++++++++++++++++++++++++ src/cx/util.h | 5 ++ tests/test_util.cc | 55 +++++++++++++---- 4 files changed, 125 insertions(+), 13 deletions(-) diff --git a/src/class_diagram/model/class_template.h b/src/class_diagram/model/class_template.h index 54f2f8b6..149292d4 100644 --- a/src/class_diagram/model/class_template.h +++ b/src/class_diagram/model/class_template.h @@ -41,6 +41,8 @@ public: friend bool operator==(const class_template &l, const class_template &r); + std::vector template_params_; + private: std::string type_; std::string name_; diff --git a/src/cx/util.cc b/src/cx/util.cc index 67c7ddcc..1779c23a 100644 --- a/src/cx/util.cc +++ b/src/cx/util.cc @@ -25,6 +25,7 @@ #include #include +#include #include namespace clanguml { @@ -294,6 +295,81 @@ const cppast::cpp_type &unreferenced(const cppast::cpp_type &t) return t; } + +std::vector +parse_unexposed_template_params(const std::string ¶ms) +{ + using class_diagram::model::class_template; + + std::vector res; + + int nested_template_level{0}; + auto it = params.begin(); + + std::string type{}; + std::vector nested_params; + bool complete_class_template{false}; + + while (it != params.end()) { + if (*it == '<') { + int nested_level{0}; + auto bracket_match_begin = it + 1; + auto bracket_match_end = bracket_match_begin; + while (bracket_match_end != params.end()) { + if (*bracket_match_end == '<') { + nested_level++; + } + else if (*bracket_match_end == '>') { + if (nested_level > 0) + nested_level--; + else + break; + } + else { + } + bracket_match_end++; + } + std::string nested_params_str( + bracket_match_begin, bracket_match_end); + nested_params = parse_unexposed_template_params(nested_params_str); + if (nested_params.empty()) + nested_params.emplace_back(class_template{nested_params_str}); + it = bracket_match_end - 1; + } + else if (*it == '>') { + complete_class_template = true; + } + else if (*it == ',') { + complete_class_template = true; + } + else { + type += *it; + } + if(complete_class_template) { + class_template t; + t.set_type(clanguml::util::trim(type)); + type = ""; + t.template_params_ = std::move(nested_params); + + res.emplace_back(std::move(t)); + complete_class_template = false; + } + it++; + } + + if(!type.empty()) { + class_template t; + t.set_type(clanguml::util::trim(type)); + type = ""; + t.template_params_ = std::move(nested_params); + + res.emplace_back(std::move(t)); + complete_class_template = false; + } + + return res; +} + } // namespace util } // namespace cx } // namespace clanguml diff --git a/src/cx/util.h b/src/cx/util.h index 182c8486..f32122d6 100644 --- a/src/cx/util.h +++ b/src/cx/util.h @@ -24,6 +24,7 @@ #include #include +#include #include namespace clanguml { @@ -66,6 +67,10 @@ std::pair split_ns( const std::string &full_name); bool is_inside_class(const cppast::cpp_entity &e); + +std::vector +parse_unexposed_template_params(const std::string ¶ms); + } // namespace util } // namespace cx } // namespace clanguml diff --git a/tests/test_util.cc b/tests/test_util.cc index 884a430b..1df6d573 100644 --- a/tests/test_util.cc +++ b/tests/test_util.cc @@ -18,6 +18,7 @@ #define CATCH_CONFIG_MAIN #include "util/util.h" +#include #include "catch.h" @@ -35,19 +36,6 @@ TEST_CASE("Test split", "[unit-test]") CHECK(split("std::vector::detail::", "::") == C{"std", "vector", "detail"}); } -// -// TEST_CASE("Test ns_relative", "[unit-test]") -//{ -// using namespace clanguml::util; -// -// CHECK(ns_relative({}, "std::vector") == "std::vector"); -// CHECK(ns_relative({"std"}, "std::vector") == "vector"); -// CHECK(ns_relative({"std"}, "const std::vector&") == "const vector&"); -// CHECK(ns_relative({"std", "clanguml::t0"}, -// "static const std::vector&") == -// "static const vector&"); -// CHECK(ns_relative({"clanguml::t0"}, "clanguml::t0") == "t0"); -//} TEST_CASE("Test abbreviate", "[unit-test]") { @@ -80,3 +68,44 @@ TEST_CASE("Test replace_all", "[unit-test]") CHECK(text == orig); } + +TEST_CASE("Test parse_unexposed_template_params", "[unit-test]") +{ + using namespace clanguml::cx::util; + + const std::string int_template_str{"ns1::ns2::class1"}; + + auto int_template = parse_unexposed_template_params(int_template_str); + + CHECK(int_template.size() == 1); + CHECK(int_template[0].template_params_.size() == 1); + CHECK(int_template[0].type() == "ns1::ns2::class1"); + CHECK(int_template[0].template_params_[0].type() == "int"); + + const std::string int_int_template_str{"ns1::ns2::class1"}; + + auto int_int_template = + parse_unexposed_template_params(int_int_template_str); + + CHECK(int_int_template.size() == 1); + CHECK(int_int_template[0].template_params_.size() == 2); + CHECK(int_int_template[0].type() == "ns1::ns2::class1"); + CHECK(int_int_template[0].template_params_[0].type() == "int"); + CHECK(int_int_template[0].template_params_[1].type() == "int"); + + const std::string nested_template_str{ + "class1>>"}; + + auto nested_template = parse_unexposed_template_params(nested_template_str); + + CHECK(nested_template.size() == 1); + CHECK(nested_template[0].template_params_.size() == 2); + CHECK(nested_template[0].type() == "class1"); + CHECK(nested_template[0].template_params_[0].type() == "int"); + const auto &class2 = nested_template[0].template_params_[1]; + CHECK(class2.type() == "ns1::class2"); + CHECK(class2.template_params_[0].type() == "int"); + CHECK(class2.template_params_[1].type() == "std::vector"); + CHECK( + class2.template_params_[1].template_params_[0].type() == "std::string"); +} From 98a118db1de7bcf92956a1e5cf7e775a091ea364 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Sun, 13 Mar 2022 12:11:55 +0100 Subject: [PATCH 02/11] Refactored unexposed template paramter parsing --- src/class_diagram/model/class.cc | 21 +------- src/class_diagram/model/class.h | 2 +- src/class_diagram/model/class_template.cc | 36 +++++++++++++ src/class_diagram/model/class_template.h | 5 ++ .../visitor/translation_unit_visitor.cc | 52 +++++++------------ src/cx/util.cc | 4 +- 6 files changed, 66 insertions(+), 54 deletions(-) diff --git a/src/class_diagram/model/class.cc b/src/class_diagram/model/class.cc index 0e05acd0..c1d068ca 100644 --- a/src/class_diagram/model/class.cc +++ b/src/class_diagram/model/class.cc @@ -62,7 +62,7 @@ void class_::add_parent(class_parent &&parent) bases_.emplace_back(std::move(parent)); } -void class_::add_template(class_template &&tmplt) +void class_::add_template(class_template tmplt) { templates_.emplace_back(std::move(tmplt)); } @@ -141,24 +141,7 @@ std::ostringstream &class_::render_template_params( std::vector tnames; std::transform(templates_.cbegin(), templates_.cend(), std::back_inserter(tnames), [this](const auto &tmplt) { - std::vector res; - - if (!tmplt.type().empty()) - res.push_back(namespace_{tmplt.type()} - .relative_to(using_namespace()) - .to_string()); - - if (!tmplt.name().empty()) - res.push_back(namespace_{tmplt.name()} - .relative_to(using_namespace()) - .to_string()); - - if (!tmplt.default_value().empty()) { - res.push_back("="); - res.push_back(tmplt.default_value()); - } - - return fmt::format("{}", fmt::join(res, " ")); + return tmplt.to_string(using_namespace()); }); ostr << fmt::format("<{}>", fmt::join(tnames, ",")); } diff --git a/src/class_diagram/model/class.h b/src/class_diagram/model/class.h index 5d17ad84..8b66d0f4 100644 --- a/src/class_diagram/model/class.h +++ b/src/class_diagram/model/class.h @@ -53,7 +53,7 @@ public: void add_member(class_member &&member); void add_method(class_method &&method); void add_parent(class_parent &&parent); - void add_template(class_template &&tmplt); + void add_template(class_template tmplt); const std::vector &members() const; const std::vector &methods() const; diff --git a/src/class_diagram/model/class_template.cc b/src/class_diagram/model/class_template.cc index 94a95e41..589bdede 100644 --- a/src/class_diagram/model/class_template.cc +++ b/src/class_diagram/model/class_template.cc @@ -17,6 +17,8 @@ */ #include "class_template.h" +#include +#include namespace clanguml::class_diagram::model { @@ -57,4 +59,38 @@ bool operator==(const class_template &l, const class_template &r) { return (l.name() == r.name()) && (l.type() == r.type()); } + +std::string class_template::to_string( + const clanguml::common::model::namespace_ &using_namespace) const +{ + using clanguml::common::model::namespace_; + + std::string res; + if (!type().empty()) + res += namespace_{type()}.relative_to(using_namespace).to_string(); + + // Render nested template params + if (!template_params_.empty()) { + std::vector params; + for (const auto &template_param : template_params_) { + params.push_back(template_param.to_string(using_namespace)); + } + + res += fmt::format("<{}>", fmt::join(params, ",")); + } + + if (!name().empty()) { + if (!type().empty()) + res += " "; + res += namespace_{name()}.relative_to(using_namespace).to_string(); + } + + if (!default_value().empty()) { + res += "="; + res += default_value(); + } + + return res; +} + } diff --git a/src/class_diagram/model/class_template.h b/src/class_diagram/model/class_template.h index 149292d4..603443e4 100644 --- a/src/class_diagram/model/class_template.h +++ b/src/class_diagram/model/class_template.h @@ -17,6 +17,8 @@ */ #pragma once +#include "common/model/namespace.h" + #include #include @@ -43,6 +45,9 @@ public: std::vector template_params_; + std::string to_string( + const clanguml::common::model::namespace_ &using_namespace) const; + private: std::string type_; std::string name_; diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index e4d41c67..c0c6f5e3 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -490,43 +490,31 @@ void translation_unit_visitor:: { auto ua = tspec.value().unexposed_arguments().as_string(); - // Naive parse of template arguments: - auto toks = util::split(ua, ","); - for (const auto &t : toks) { - c.add_template({t}); + auto template_params = cx::util::parse_unexposed_template_params(ua); + for (const auto ¶m : template_params) { + c.add_template(param); + } - if (!tspec.value().primary_template().is_overloaded()) { - if (tspec.value() - .primary_template() - .get(ctx.entity_index()) - .size() == 0) { - LOG_WARN("Template {} has no exposed parameters", - tspec.value().name()); - - continue; - } - } - - const auto &primary_template_ref = static_cast< - const cppast::cpp_class_template &>( + const auto &primary_template_ref = + static_cast( tspec.value().primary_template().get(ctx.entity_index())[0].get()) - .class_(); + .class_(); - if (primary_template_ref.user_data()) { - auto base_template_full_name = - static_cast(primary_template_ref.user_data()); - LOG_DBG("Primary template ref set to: {}", base_template_full_name); - // Add template specialization/instantiation - // relationship - c.add_relationship( - {relationship_t::kInstantiation, base_template_full_name}); - } - else { - LOG_WARN("No user data for base template {}", - primary_template_ref.name()); - } + if (primary_template_ref.user_data()) { + auto base_template_full_name = + static_cast(primary_template_ref.user_data()); + LOG_DBG("Primary template ref set to: {}", base_template_full_name); + // Add template specialization/instantiation + // relationship + c.add_relationship( + {relationship_t::kInstantiation, base_template_full_name}); + } + else { + LOG_WARN( + "No user data for base template {}", primary_template_ref.name()); } } + void translation_unit_visitor::process_class_bases( const cppast::cpp_class &cls, class_ &c) const { diff --git a/src/cx/util.cc b/src/cx/util.cc index 1779c23a..d462dc11 100644 --- a/src/cx/util.cc +++ b/src/cx/util.cc @@ -345,7 +345,7 @@ parse_unexposed_template_params(const std::string ¶ms) else { type += *it; } - if(complete_class_template) { + if (complete_class_template) { class_template t; t.set_type(clanguml::util::trim(type)); type = ""; @@ -357,7 +357,7 @@ parse_unexposed_template_params(const std::string ¶ms) it++; } - if(!type.empty()) { + if (!type.empty()) { class_template t; t.set_type(clanguml::util::trim(type)); type = ""; From 40dec399956fc2de0f398d669de34c13ff0f4b14 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Sun, 13 Mar 2022 23:33:08 +0100 Subject: [PATCH 03/11] 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); From 82672b108280915e8db163054ef16d85ac100f6c Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Sun, 13 Mar 2022 23:36:51 +0100 Subject: [PATCH 04/11] Updated test cases documentation --- docs/test_cases/t00034_class.svg | 138 +++++++++++------------ docs/test_cases/t00038.md | 7 +- docs/test_cases/t00038_class.svg | 170 +++++++++++++++-------------- docs/test_cases/t30001_package.svg | 2 +- docs/test_cases/t30004_package.svg | 10 +- docs/test_cases/t30006_package.svg | 4 +- docs/test_cases/t30007_package.svg | 4 +- 7 files changed, 173 insertions(+), 162 deletions(-) diff --git a/docs/test_cases/t00034_class.svg b/docs/test_cases/t00034_class.svg index 886adb30..efbd5bf5 100644 --- a/docs/test_cases/t00034_class.svg +++ b/docs/test_cases/t00034_class.svg @@ -1,6 +1,6 @@ - + - + @@ -8,71 +8,73 @@ - - - - Void - - - - operator==(Void const& ) const : bool - - operator!=(Void const& ) const : bool - - - - lift_void - - T - - - - - - lift_void - - void - - - - - - drop_void - - T - - - - - - drop_void - - Void - - - - - - A - - - - - - R - - - la : lift_void_t<A>* - - lv : lift_void_t<void>* - - - - - - - - - la + + + + Void + + + + operator==(Void const& ) const : bool + + operator!=(Void const& ) const : bool + + + + lift_void + + T + + + + + + lift_void + + void + + + + + + drop_void + + T + + + + + + drop_void + + Void + + + + + + A + + + + + + R + + + la : lift_void_t<A>* + + lv : lift_void_t<void>* + + + + + + + + + + + la diff --git a/docs/test_cases/t00038.md b/docs/test_cases/t00038.md index 7e692ca2..0650b30e 100644 --- a/docs/test_cases/t00038.md +++ b/docs/test_cases/t00038.md @@ -42,12 +42,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/docs/test_cases/t00038_class.svg b/docs/test_cases/t00038_class.svg index d98ca2dd..52bf6833 100644 --- a/docs/test_cases/t00038_class.svg +++ b/docs/test_cases/t00038_class.svg @@ -1,6 +1,6 @@ - + - + @@ -8,84 +8,92 @@ - - - - property_t - - property_a - property_b - property_c - - - - - A - - - - - - B - - - - - - C - - - - - - key_t - - - key : std::string - - - - - map - - T - - - - - - map - - std::integral_constant<property_t,property_t::property_a> - - - - - - map - - std::vector<std::integral_constant<property_t,property_t::property_b>> - - - - - - map - - std::map<key_t,std::vector<std::integral_constant<property_t,property_t::property_c>>> - - - - - - - - - - - - - - + + + + property_t + + property_a + property_b + property_c + + + + + A + + + + + + B + + + + + + C + + + + + + key_t + + + key : std::string + + + + + map + + T + + + + + + map + + std::integral_constant<property_t,property_t::property_a> + + + + + + map + + std::vector<std::integral_constant<t00038::property_t,t00038::property_t::property_b>> + + + + + + map + + std::map<key_t,std::vector<std::integral_constant<property_t,property_t::property_c>>> + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/test_cases/t30001_package.svg b/docs/test_cases/t30001_package.svg index 5a619080..ff2f973b 100644 --- a/docs/test_cases/t30001_package.svg +++ b/docs/test_cases/t30001_package.svg @@ -41,6 +41,6 @@ A AAA note... - + diff --git a/docs/test_cases/t30004_package.svg b/docs/test_cases/t30004_package.svg index fa521c04..3f4be825 100644 --- a/docs/test_cases/t30004_package.svg +++ b/docs/test_cases/t30004_package.svg @@ -38,10 +38,10 @@ EEE - - - - - + + + + + diff --git a/docs/test_cases/t30006_package.svg b/docs/test_cases/t30006_package.svg index 9e9a5585..0d761d54 100644 --- a/docs/test_cases/t30006_package.svg +++ b/docs/test_cases/t30006_package.svg @@ -23,8 +23,8 @@ Bottom A note. - - + + diff --git a/docs/test_cases/t30007_package.svg b/docs/test_cases/t30007_package.svg index e86c10b7..b49f927f 100644 --- a/docs/test_cases/t30007_package.svg +++ b/docs/test_cases/t30007_package.svg @@ -26,8 +26,8 @@ Bottom A note. - - + + From 1d0bef2085f4895406fdb4902e9104a4be41863d Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Mon, 14 Mar 2022 21:36:11 +0100 Subject: [PATCH 05/11] Add apt db update step to build action --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 47ee668b..52352601 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,8 +10,10 @@ jobs: uses: actions/checkout@v2 with: submodules: recursive + - name: Update package database + run: sudo apt -y update - name: Install deps - run: sudo apt install git make gcc-10 g++-10 ccache cmake libyaml-cpp-dev libfmt-dev libspdlog-dev clang-12 libclang-12-dev libclang-cpp12-dev + run: sudo apt -y install git make gcc-10 g++-10 ccache cmake libyaml-cpp-dev libfmt-dev libspdlog-dev clang-12 libclang-12-dev libclang-cpp12-dev - name: Select g++ version run: | sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 10 From c0a759c2c4d8d347a4a0b4a40cb8dba478acb15d Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Mon, 14 Mar 2022 23:23:48 +0100 Subject: [PATCH 06/11] Added storing list of using namespace directives for each namespace --- .../visitor/translation_unit_context.cc | 13 +++++++++++ .../visitor/translation_unit_context.h | 11 ++++++++++ .../visitor/translation_unit_visitor.cc | 2 ++ src/common/model/namespace.cc | 5 +++++ src/common/model/namespace.h | 22 ++++++++++++++++++- 5 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/class_diagram/visitor/translation_unit_context.cc b/src/class_diagram/visitor/translation_unit_context.cc index ae2fe3fb..3e200753 100644 --- a/src/class_diagram/visitor/translation_unit_context.cc +++ b/src/class_diagram/visitor/translation_unit_context.cc @@ -196,4 +196,17 @@ translation_unit_context::get_current_package() const return current_package_; } +void translation_unit_context::add_using_namespace_directive( + common::model::namespace_ ns) +{ + using_ns_declarations_[ns_.to_string()].insert(std::move(ns)); +} + +const std::set & +translation_unit_context::using_namespace_directive( + const common::model::namespace_ &ns) const +{ + return using_ns_declarations_.at(ns.to_string()); +} + } diff --git a/src/class_diagram/visitor/translation_unit_context.h b/src/class_diagram/visitor/translation_unit_context.h index 7775d89d..188eb1cf 100644 --- a/src/class_diagram/visitor/translation_unit_context.h +++ b/src/class_diagram/visitor/translation_unit_context.h @@ -78,10 +78,21 @@ public: type_safe::optional_ref get_current_package() const; + void add_using_namespace_directive(common::model::namespace_ ns); + + const std::set &using_namespace_directive( + const common::model::namespace_ &ns) const; + private: // Current visitor namespace common::model::namespace_ ns_; + // A map of 'using namespace' declared within a given namespace scope + // This is necessary to properly establish the namespace of a given entity + // for instance in unexposed template parameters + std::map> + using_ns_declarations_; + // Reference to the cppast entity index cppast::cpp_entity_index &entity_index_; diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 82cfc8cf..10394118 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -187,6 +187,8 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file) 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()); + + ctx.add_using_namespace_directive(full_ns); } } }); diff --git a/src/common/model/namespace.cc b/src/common/model/namespace.cc index 8980eb12..37f42a5f 100644 --- a/src/common/model/namespace.cc +++ b/src/common/model/namespace.cc @@ -185,6 +185,11 @@ bool operator==(const namespace_ &left, const namespace_ &right) return left.namespace_path_ == right.namespace_path_; } +bool operator<(const namespace_ &left, const namespace_ &right) +{ + return std::hash{}(left) < std::hash{}(right); +} + std::string namespace_::name() const { assert(size() > 0); diff --git a/src/common/model/namespace.h b/src/common/model/namespace.h index a6fc88a6..e8c19c82 100644 --- a/src/common/model/namespace.h +++ b/src/common/model/namespace.h @@ -23,9 +23,9 @@ namespace clanguml::common::model { class namespace_ { +public: using container_type = std::vector; -public: namespace_() = default; namespace_(const std::string &ns); @@ -42,6 +42,7 @@ public: namespace_ &operator=(namespace_ &&right) noexcept = default; friend bool operator==(const namespace_ &left, const namespace_ &right); + friend bool operator<(const namespace_ &left, const namespace_ &right); namespace_(std::initializer_list ns); @@ -85,4 +86,23 @@ private: container_type namespace_path_; }; +} + +namespace std { + +template <> struct hash { + std::size_t operator()(const clanguml::common::model::namespace_ &key) const + { + using clanguml::common::model::namespace_; + + std::size_t seed = key.size(); + for (const auto &ns : key) { + seed ^= std::hash{}(ns) + 0x6a3712b5 + (seed << 6) + + (seed >> 2); + } + + return seed; + } +}; + } \ No newline at end of file From 3a7d22d527c73c5b63743e0cae9dd4e54a7810af Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Wed, 16 Mar 2022 23:49:58 +0100 Subject: [PATCH 07/11] Fixed unexposed template parameters namespace resolution --- src/class_diagram/model/enum.cc | 6 +- .../visitor/translation_unit_context.cc | 55 +++++++++++++++++++ .../visitor/translation_unit_context.h | 5 ++ .../visitor/translation_unit_visitor.cc | 49 +++++++---------- src/common/model/namespace.cc | 11 ++++ src/common/model/namespace.h | 3 + src/cx/util.cc | 10 ++-- src/cx/util.h | 3 +- tests/t00038/.clang-uml | 5 +- tests/t00038/t00038.cc | 18 ++++++ tests/t00038/test_case.h | 16 ++++-- tests/test_cases.h | 1 + tests/test_util.cc | 10 ++-- 13 files changed, 145 insertions(+), 47 deletions(-) diff --git a/src/class_diagram/model/enum.cc b/src/class_diagram/model/enum.cc index 2343559f..41bbdb77 100644 --- a/src/class_diagram/model/enum.cc +++ b/src/class_diagram/model/enum.cc @@ -41,9 +41,11 @@ std::string enum_::full_name(bool relative) const std::ostringstream ostr; if (relative) - ostr << namespace_{name()}.relative_to(using_namespace()).to_string(); + ostr << namespace_{name_and_ns()} + .relative_to(using_namespace()) + .to_string(); else - ostr << name(); + ostr << name_and_ns(); return ostr.str(); } diff --git a/src/class_diagram/visitor/translation_unit_context.cc b/src/class_diagram/visitor/translation_unit_context.cc index 3e200753..0031d010 100644 --- a/src/class_diagram/visitor/translation_unit_context.cc +++ b/src/class_diagram/visitor/translation_unit_context.cc @@ -209,4 +209,59 @@ translation_unit_context::using_namespace_directive( return using_ns_declarations_.at(ns.to_string()); } +type_safe::optional +translation_unit_context::get_name_with_namespace(const std::string &name) const +{ + using common::model::namespace_; + + std::set possible_matches; + possible_matches.emplace(name); + + possible_matches.emplace(get_namespace() | namespace_{name}); + auto parent = get_namespace().parent(); + while (parent.has_value()) { + possible_matches.emplace(parent.value() | namespace_{name}); + parent = parent.value().parent(); + } + + if (using_ns_declarations_.find(get_namespace().to_string()) != + using_ns_declarations_.end()) { + for (const auto &ns : + using_ns_declarations_.at(get_namespace().to_string())) { + possible_matches.emplace(ns | namespace_{name}); + auto parent = ns.parent(); + while (parent.has_value()) { + possible_matches.emplace(parent.value() | namespace_{name}); + parent = parent.value().parent(); + } + } + } + + // Search classes + for (const auto &c : diagram_.classes()) { + auto c_ns = namespace_{c->name_and_ns()}; + for (const auto &possible_match : possible_matches) { + if (c_ns == possible_match) { + return possible_match; + } + } + } + + // Search enums + for (const auto &e : diagram_.enums()) { + auto e_ns = namespace_{e->name_and_ns()}; + for (const auto &possible_match : possible_matches) { + if (e_ns == possible_match) { + return possible_match; + } + // Try to also match possible references to enum values + else if (possible_match.starts_with(e_ns)) { + return possible_match; + } + } + } + + return {}; +} + } diff --git a/src/class_diagram/visitor/translation_unit_context.h b/src/class_diagram/visitor/translation_unit_context.h index 188eb1cf..714f7eae 100644 --- a/src/class_diagram/visitor/translation_unit_context.h +++ b/src/class_diagram/visitor/translation_unit_context.h @@ -83,6 +83,9 @@ public: const std::set &using_namespace_directive( const common::model::namespace_ &ns) const; + type_safe::optional get_name_with_namespace( + const std::string &name) const; + private: // Current visitor namespace common::model::namespace_ ns_; @@ -90,6 +93,8 @@ private: // A map of 'using namespace' declared within a given namespace scope // This is necessary to properly establish the namespace of a given entity // for instance in unexposed template parameters + // - key - namespace + // - value - set of namespaces 'imported' within this namespace scope std::map> using_ns_declarations_; diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 10394118..03debefb 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -179,14 +179,15 @@ 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) { + using common::model::namespace_; const auto &using_directive = static_cast(e); const auto ns_ref = using_directive.target(); + const auto &ns = ns_ref.get(ctx.entity_index()).at(0).get(); 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()); + auto full_ns = namespace_{cx::util::ns(ns)} | ns.name(); ctx.add_using_namespace_directive(full_ns); } @@ -503,10 +504,16 @@ void translation_unit_visitor:: { auto ua = tspec.value().unexposed_arguments().as_string(); - auto template_params = cx::util::parse_unexposed_template_params(ua); + auto template_params = cx::util::parse_unexposed_template_params( + ua, [this](const std::string &t) { + auto full_type = ctx.get_name_with_namespace(t); + if (full_type.has_value()) + return full_type.value().to_string(); + return t; + }); found_relationships_t relationships; - for (const auto ¶m : template_params) { + for (auto ¶m : template_params) { find_relationships_in_unexposed_template_params(param, relationships); c.add_template(param); } @@ -1414,31 +1421,17 @@ bool translation_unit_visitor::find_relationships_in_unexposed_template_params( 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 type_with_namespace = ctx.get_name_with_namespace(ct.type()); - 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; - } - } + if (!type_with_namespace.has_value()) { + // Couldn't find declaration of this type + type_with_namespace = common::model::namespace_{ct.type()}; + } + + if (ctx.config().should_include(type_with_namespace.value())) { + relationships.emplace_back(type_with_namespace.value().to_string(), + relationship_t::kDependency); + found = true; } for (const auto &nested_template_params : ct.template_params_) { diff --git a/src/common/model/namespace.cc b/src/common/model/namespace.cc index 37f42a5f..ed25e50a 100644 --- a/src/common/model/namespace.cc +++ b/src/common/model/namespace.cc @@ -99,6 +99,17 @@ void namespace_::append(const namespace_ &ns) void namespace_::pop_back() { namespace_path_.pop_back(); } +type_safe::optional namespace_::parent() const +{ + if (size() <= 1) { + return {}; + } + + namespace_ res{*this}; + res.pop_back(); + return {std::move(res)}; +} + namespace_ namespace_::operator|(const namespace_ &right) const { namespace_ res{*this}; diff --git a/src/common/model/namespace.h b/src/common/model/namespace.h index e8c19c82..03d42bc9 100644 --- a/src/common/model/namespace.h +++ b/src/common/model/namespace.h @@ -18,6 +18,7 @@ #pragma once #include +#include #include namespace clanguml::common::model { @@ -68,6 +69,8 @@ public: void pop_back(); + type_safe::optional parent() const; + bool starts_with(const namespace_ &right) const; bool ends_with(const namespace_ &right) const; namespace_ common_path(const namespace_ &right) const; diff --git a/src/cx/util.cc b/src/cx/util.cc index d462dc11..157eee80 100644 --- a/src/cx/util.cc +++ b/src/cx/util.cc @@ -297,7 +297,8 @@ const cppast::cpp_type &unreferenced(const cppast::cpp_type &t) } std::vector -parse_unexposed_template_params(const std::string ¶ms) +parse_unexposed_template_params(const std::string ¶ms, + std::function ns_resolve) { using class_diagram::model::class_template; @@ -331,7 +332,8 @@ parse_unexposed_template_params(const std::string ¶ms) } std::string nested_params_str( bracket_match_begin, bracket_match_end); - nested_params = parse_unexposed_template_params(nested_params_str); + nested_params = + parse_unexposed_template_params(nested_params_str, ns_resolve); if (nested_params.empty()) nested_params.emplace_back(class_template{nested_params_str}); it = bracket_match_end - 1; @@ -347,7 +349,7 @@ parse_unexposed_template_params(const std::string ¶ms) } if (complete_class_template) { class_template t; - t.set_type(clanguml::util::trim(type)); + t.set_type(ns_resolve(clanguml::util::trim(type))); type = ""; t.template_params_ = std::move(nested_params); @@ -359,7 +361,7 @@ parse_unexposed_template_params(const std::string ¶ms) if (!type.empty()) { class_template t; - t.set_type(clanguml::util::trim(type)); + t.set_type(ns_resolve(clanguml::util::trim(type))); type = ""; t.template_params_ = std::move(nested_params); diff --git a/src/cx/util.h b/src/cx/util.h index f32122d6..65118171 100644 --- a/src/cx/util.h +++ b/src/cx/util.h @@ -69,7 +69,8 @@ std::pair split_ns( bool is_inside_class(const cppast::cpp_entity &e); std::vector -parse_unexposed_template_params(const std::string ¶ms); +parse_unexposed_template_params(const std::string ¶ms, + std::function ns_resolve); } // namespace util } // namespace cx diff --git a/tests/t00038/.clang-uml b/tests/t00038/.clang-uml index 29318d35..a0f321ac 100644 --- a/tests/t00038/.clang-uml +++ b/tests/t00038/.clang-uml @@ -3,11 +3,12 @@ output_directory: puml diagrams: t00038_class: type: class - generate_packages: true + generate_packages: false glob: - ../../tests/t00038/t00038.cc using_namespace: - clanguml::t00038 include: namespaces: - - clanguml::t00038 \ No newline at end of file + - clanguml::t00038 + - thirdparty::ns1 \ No newline at end of file diff --git a/tests/t00038/t00038.cc b/tests/t00038/t00038.cc index 63dec538..e33b091e 100644 --- a/tests/t00038/t00038.cc +++ b/tests/t00038/t00038.cc @@ -3,6 +3,19 @@ #include #include +namespace thirdparty { +namespace ns1 { +enum class color_t { red, green, blue }; + +struct E { +}; +} // namespace ns1 +namespace ns2 { +struct F { +}; +} // namespace ns2 +} // namespace thirdparty + namespace clanguml { namespace t00038 { @@ -21,6 +34,11 @@ struct key_t { template struct map; +using namespace thirdparty::ns1; + +template <> struct map> : E { +}; + template <> struct map> : A { diff --git a/tests/t00038/test_case.h b/tests/t00038/test_case.h index 102f4982..512f1a39 100644 --- a/tests/t00038/test_case.h +++ b/tests/t00038/test_case.h @@ -23,7 +23,7 @@ TEST_CASE("t00038", "[test-case][class]") auto diagram = config.diagrams["t00038_class"]; REQUIRE(diagram->name == "t00038_class"); - REQUIRE(diagram->generate_packages() == true); + REQUIRE(diagram->generate_packages() == false); auto model = generate_class_diagram(db, diagram); @@ -38,6 +38,7 @@ TEST_CASE("t00038", "[test-case][class]") REQUIRE_THAT(puml, IsClass(_A("A"))); REQUIRE_THAT(puml, IsClass(_A("B"))); REQUIRE_THAT(puml, IsClass(_A("C"))); + REQUIRE_THAT(puml, IsClass(_A("thirdparty::ns1::E"))); REQUIRE_THAT(puml, IsClass(_A("key_t"))); REQUIRE_THAT(puml, IsClassTemplate("map", "T")); REQUIRE_THAT(puml, @@ -45,8 +46,7 @@ TEST_CASE("t00038", "[test-case][class]") "std::integral_constant")); REQUIRE_THAT(puml, IsClassTemplate("map", - "std::vector>")); REQUIRE_THAT(puml, IsClassTemplate("map", @@ -70,9 +70,8 @@ TEST_CASE("t00038", "[test-case][class]") REQUIRE_THAT(puml, IsDependency(_A("map<" - "std::vector>>"), + "std::vector>>"), _A("property_t"))); REQUIRE_THAT(puml, @@ -85,6 +84,11 @@ TEST_CASE("t00038", "[test-case][class]") "property_t,property_t::property_c>>>>"), _A("key_t"))); + REQUIRE_THAT(puml, + IsDependency(_A("map>"), + _A("thirdparty::ns1::color_t"))); + save_puml( "./" + config.output_directory() + "/" + diagram->name + ".puml", puml); } diff --git a/tests/test_cases.h b/tests/test_cases.h index b20dbd8b..7d6e20fd 100644 --- a/tests/test_cases.h +++ b/tests/test_cases.h @@ -31,6 +31,7 @@ #include "util/util.h" #define CATCH_CONFIG_RUNNER +#define CATCH_CONFIG_CONSOLE_WIDTH 512 #include "catch.h" diff --git a/tests/test_util.cc b/tests/test_util.cc index 1df6d573..6e9be6fd 100644 --- a/tests/test_util.cc +++ b/tests/test_util.cc @@ -75,7 +75,8 @@ TEST_CASE("Test parse_unexposed_template_params", "[unit-test]") const std::string int_template_str{"ns1::ns2::class1"}; - auto int_template = parse_unexposed_template_params(int_template_str); + auto int_template = parse_unexposed_template_params( + int_template_str, [](const auto &n) { return n; }); CHECK(int_template.size() == 1); CHECK(int_template[0].template_params_.size() == 1); @@ -84,8 +85,8 @@ TEST_CASE("Test parse_unexposed_template_params", "[unit-test]") const std::string int_int_template_str{"ns1::ns2::class1"}; - auto int_int_template = - parse_unexposed_template_params(int_int_template_str); + auto int_int_template = parse_unexposed_template_params( + int_int_template_str, [](const auto &n) { return n; }); CHECK(int_int_template.size() == 1); CHECK(int_int_template[0].template_params_.size() == 2); @@ -96,7 +97,8 @@ TEST_CASE("Test parse_unexposed_template_params", "[unit-test]") const std::string nested_template_str{ "class1>>"}; - auto nested_template = parse_unexposed_template_params(nested_template_str); + auto nested_template = parse_unexposed_template_params( + nested_template_str, [](const auto &n) { return n; }); CHECK(nested_template.size() == 1); CHECK(nested_template[0].template_params_.size() == 2); From 23cdcc8ee4b3b012e132f4227c066a68651ae627 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Wed, 16 Mar 2022 23:51:36 +0100 Subject: [PATCH 08/11] Updated test cases documentation --- docs/test_cases/t00038.md | 21 ++- docs/test_cases/t00038_class.svg | 199 ++++++++++++++++------------- docs/test_cases/t30001_package.svg | 26 ++-- docs/test_cases/t30002_package.svg | 64 +++++----- docs/test_cases/t30003_package.svg | 16 +-- docs/test_cases/t30004_package.svg | 32 ++--- docs/test_cases/t30005_package.svg | 24 ++-- docs/test_cases/t30006_package.svg | 20 +-- docs/test_cases/t30007_package.svg | 22 ++-- 9 files changed, 235 insertions(+), 189 deletions(-) diff --git a/docs/test_cases/t00038.md b/docs/test_cases/t00038.md index 0650b30e..006ea523 100644 --- a/docs/test_cases/t00038.md +++ b/docs/test_cases/t00038.md @@ -6,7 +6,7 @@ output_directory: puml diagrams: t00038_class: type: class - generate_packages: true + generate_packages: false glob: - ../../tests/t00038/t00038.cc using_namespace: @@ -14,6 +14,7 @@ diagrams: include: namespaces: - clanguml::t00038 + - thirdparty::ns1 ``` ## Source code File t00038.cc @@ -23,6 +24,19 @@ File t00038.cc #include #include +namespace thirdparty { +namespace ns1 { +enum class color_t { red, green, blue }; + +struct E { +}; +} // namespace ns1 +namespace ns2 { +struct F { +}; +} // namespace ns2 +} // namespace thirdparty + namespace clanguml { namespace t00038 { @@ -41,6 +55,11 @@ struct key_t { template struct map; +using namespace thirdparty::ns1; + +template <> struct map> : E { +}; + template <> struct map> : A { diff --git a/docs/test_cases/t00038_class.svg b/docs/test_cases/t00038_class.svg index 52bf6833..6422a395 100644 --- a/docs/test_cases/t00038_class.svg +++ b/docs/test_cases/t00038_class.svg @@ -1,6 +1,6 @@ - + - + @@ -8,92 +8,119 @@ - - - - property_t - - property_a - property_b - property_c - - - - - A - - - - - - B - - - - - - C - - - - - - key_t - - - key : std::string - - - - - map - - T - - - - - - map - - std::integral_constant<property_t,property_t::property_a> - - - + + + + thirdparty::ns1::color_t + + red + green + blue + + + + + thirdparty::ns1::E + + + + + + property_t + + property_a + property_b + property_c + + + + + A + + + + + + B + + + + + + C + + + + + + key_t + + + key : std::string + + + + + map + + T + + + map - - std::vector<std::integral_constant<t00038::property_t,t00038::property_t::property_b>> - - - - - - map - - std::map<key_t,std::vector<std::integral_constant<property_t,property_t::property_c>>> - - - - - - - - - - - - - - - - - - - - - - + + std::integral_constant<thirdparty::ns1::color_t,thirdparty::ns1::color_t::red> + + + + + + map + + std::integral_constant<property_t,property_t::property_a> + + + + + + map + + std::vector<std::integral_constant<property_t,property_t::property_b>> + + + + + + map + + std::map<key_t,std::vector<std::integral_constant<property_t,property_t::property_c>>> + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/test_cases/t30001_package.svg b/docs/test_cases/t30001_package.svg index ff2f973b..3de96c8a 100644 --- a/docs/test_cases/t30001_package.svg +++ b/docs/test_cases/t30001_package.svg @@ -1,6 +1,6 @@ - + @@ -8,39 +8,39 @@ - + A - + AA - + B - + AA - + AAA - + BBB - + BB - + AAA - + BBB - + BB - + A AAA note... - + diff --git a/docs/test_cases/t30002_package.svg b/docs/test_cases/t30002_package.svg index edba7654..7eefba6b 100644 --- a/docs/test_cases/t30002_package.svg +++ b/docs/test_cases/t30002_package.svg @@ -1,6 +1,6 @@ - + @@ -8,85 +8,85 @@ - + A - + AA - + B - + BB - + A1 - + A2 - + A3 - + A4 - + A5 - + A6 - + A7 - + A8 - + A9 - + A10 - + A11 - + A12 - + A13 - + BBB - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/docs/test_cases/t30003_package.svg b/docs/test_cases/t30003_package.svg index 80850b58..62d4b5b3 100644 --- a/docs/test_cases/t30003_package.svg +++ b/docs/test_cases/t30003_package.svg @@ -1,6 +1,6 @@ - + @@ -8,27 +8,27 @@ - + ns1 - + ns3 «deprecated» - + ns1 - + ns2_v1_0_0 - + ns2_v0_9_0 «deprecated» - + ns2 - + diff --git a/docs/test_cases/t30004_package.svg b/docs/test_cases/t30004_package.svg index 3f4be825..c63ca8b5 100644 --- a/docs/test_cases/t30004_package.svg +++ b/docs/test_cases/t30004_package.svg @@ -1,6 +1,6 @@ - + @@ -8,40 +8,40 @@ - + A - + Package AAA. - + Package BBB. - + CCCC package note. - + Another CCC note. - + We skipped DDD. - + AAA - + BBB - + CCC - + EEE - - - - - + + + + + diff --git a/docs/test_cases/t30005_package.svg b/docs/test_cases/t30005_package.svg index af71db87..c84f1cd8 100644 --- a/docs/test_cases/t30005_package.svg +++ b/docs/test_cases/t30005_package.svg @@ -1,6 +1,6 @@ - + @@ -8,36 +8,36 @@ - + A - + AA - + B - + BB - + C - + CC - + AAA - + BBB - + CCC - + - + diff --git a/docs/test_cases/t30006_package.svg b/docs/test_cases/t30006_package.svg index 0d761d54..57c723ed 100644 --- a/docs/test_cases/t30006_package.svg +++ b/docs/test_cases/t30006_package.svg @@ -1,6 +1,6 @@ - + @@ -8,26 +8,26 @@ - + B - + A - + C - + Top A note. - + Bottom A note. - - - + + + - + diff --git a/docs/test_cases/t30007_package.svg b/docs/test_cases/t30007_package.svg index b49f927f..6ffe8463 100644 --- a/docs/test_cases/t30007_package.svg +++ b/docs/test_cases/t30007_package.svg @@ -1,6 +1,6 @@ - + @@ -8,29 +8,29 @@ - + A - + B - + AA - + C - + Compare layout with t30006. - + Bottom A note. - - - + + + - + From 331f3310ce1261f110b44ae8d2445f58749546cf Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Thu, 17 Mar 2022 00:14:48 +0100 Subject: [PATCH 09/11] Fixed namespace resolution of base classes --- src/class_diagram/visitor/translation_unit_visitor.cc | 5 ++++- tests/t00038/test_case.h | 8 +++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 03debefb..bb58e44d 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -547,7 +547,10 @@ void translation_unit_visitor::process_class_bases( { for (auto &base : cls.bases()) { class_parent cp; - cp.set_name(cx::util::fully_prefixed(ctx.get_namespace(), base)); + auto base_ns = common::model::namespace_{ + cx::util::ns(base.type(), ctx.entity_index())}; + base_ns = base_ns | base.name(); + cp.set_name(base_ns.to_string()); cp.is_virtual(base.is_virtual()); switch (base.access_specifier()) { diff --git a/tests/t00038/test_case.h b/tests/t00038/test_case.h index 512f1a39..8ca2b260 100644 --- a/tests/t00038/test_case.h +++ b/tests/t00038/test_case.h @@ -60,9 +60,6 @@ TEST_CASE("t00038", "[test-case][class]") _A("map>>>"))); - // TODO: Add parsing of unexposed template arguments to infer - // additional relationships - REQUIRE_THAT(puml, IsDependency(_A("map>"), @@ -89,6 +86,11 @@ TEST_CASE("t00038", "[test-case][class]") "thirdparty::ns1::color_t::red>>"), _A("thirdparty::ns1::color_t"))); + REQUIRE_THAT(puml, + IsBaseClass(_A("thirdparty::ns1::E"), + _A("map>"))); + save_puml( "./" + config.output_directory() + "/" + diagram->name + ".puml", puml); } From 7abada2f6bcdde6ddde583275eb746e6c1b1f20d Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Thu, 17 Mar 2022 00:55:19 +0100 Subject: [PATCH 10/11] Fixed namespace namespace resolution --- src/class_diagram/visitor/translation_unit_visitor.cc | 5 +++-- src/cx/util.cc | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index bb58e44d..c76616a4 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -549,8 +549,9 @@ void translation_unit_visitor::process_class_bases( class_parent cp; auto base_ns = common::model::namespace_{ cx::util::ns(base.type(), ctx.entity_index())}; - base_ns = base_ns | base.name(); - cp.set_name(base_ns.to_string()); + base_ns = base_ns | common::model::namespace_{base.name()}.name(); + cp.set_name( + base_ns.relative_to(ctx.config().using_namespace()).to_string()); cp.is_virtual(base.is_virtual()); switch (base.access_specifier()) { diff --git a/src/cx/util.cc b/src/cx/util.cc index 157eee80..f43859a0 100644 --- a/src/cx/util.cc +++ b/src/cx/util.cc @@ -125,7 +125,9 @@ std::string ns(const cppast::cpp_entity &e) auto it = e.parent(); while (it) { if (it.value().kind() == cppast::cpp_entity_kind::namespace_t) { - if (!it.value().name().empty()) + const auto &ns = + static_cast(it.value()); + if (!ns.name().empty() && !ns.is_inline()) res.push_back(it.value().name()); } it = it.value().parent(); From e88a977856282192c627c23e20fc9bc2e0514328 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Thu, 17 Mar 2022 00:56:30 +0100 Subject: [PATCH 11/11] Updated test cases documentation --- docs/test_cases/t00038_class.svg | 38 ++++++++++++++++-------------- docs/test_cases/t30001_package.svg | 2 +- docs/test_cases/t30004_package.svg | 10 ++++---- docs/test_cases/t30006_package.svg | 4 ++-- docs/test_cases/t30007_package.svg | 4 ++-- 5 files changed, 30 insertions(+), 28 deletions(-) diff --git a/docs/test_cases/t00038_class.svg b/docs/test_cases/t00038_class.svg index 6422a395..94f88878 100644 --- a/docs/test_cases/t00038_class.svg +++ b/docs/test_cases/t00038_class.svg @@ -1,6 +1,6 @@ - + @@ -8,7 +8,7 @@ - + thirdparty::ns1::color_t @@ -17,13 +17,13 @@ green blue - - - - thirdparty::ns1::E - - - + + + + thirdparty::ns1::E + + + property_t @@ -32,25 +32,25 @@ property_b property_c - + A - + B - + C - + key_t @@ -58,7 +58,7 @@ key : std::string - + map @@ -66,7 +66,7 @@ T - + map @@ -74,7 +74,7 @@ std::integral_constant<thirdparty::ns1::color_t,thirdparty::ns1::color_t::red> - + map @@ -82,7 +82,7 @@ std::integral_constant<property_t,property_t::property_a> - + map @@ -90,7 +90,7 @@ std::vector<std::integral_constant<property_t,property_t::property_b>> - + map @@ -102,6 +102,8 @@ + + diff --git a/docs/test_cases/t30001_package.svg b/docs/test_cases/t30001_package.svg index 3de96c8a..0ed4b719 100644 --- a/docs/test_cases/t30001_package.svg +++ b/docs/test_cases/t30001_package.svg @@ -41,6 +41,6 @@ A AAA note... - + diff --git a/docs/test_cases/t30004_package.svg b/docs/test_cases/t30004_package.svg index c63ca8b5..30bb4f79 100644 --- a/docs/test_cases/t30004_package.svg +++ b/docs/test_cases/t30004_package.svg @@ -38,10 +38,10 @@ EEE - - - - - + + + + + diff --git a/docs/test_cases/t30006_package.svg b/docs/test_cases/t30006_package.svg index 57c723ed..8ea18eea 100644 --- a/docs/test_cases/t30006_package.svg +++ b/docs/test_cases/t30006_package.svg @@ -23,8 +23,8 @@ Bottom A note. - - + + diff --git a/docs/test_cases/t30007_package.svg b/docs/test_cases/t30007_package.svg index 6ffe8463..3d42f821 100644 --- a/docs/test_cases/t30007_package.svg +++ b/docs/test_cases/t30007_package.svg @@ -26,8 +26,8 @@ Bottom A note. - - + +