From ec974148700f9ce8052736ae05c41ca127b3d2a1 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Sat, 21 May 2022 20:36:35 +0200 Subject: [PATCH] Refactored template argument relationship hints to configuration file option --- .../visitor/translation_unit_visitor.cc | 54 +++++++-------- src/config/config.cc | 68 +++++++++++++++++++ src/config/config.h | 24 ++++++- tests/test_config.cc | 21 ++++++ tests/test_config_data/simple.yml | 12 +++- 5 files changed, 146 insertions(+), 33 deletions(-) diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 1158aa05..4cacd599 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -1487,30 +1487,9 @@ bool translation_unit_visitor::find_relationships_in_template_instantiation( auto full_name = fmt::format("{}", fmt::join(ns_and_name, "::")); - // Try to match common containers - // TODO: Refactor to a separate class with configurable - // container list - if (full_name.find("std::unique_ptr") == 0) { - found = find_relationships(args[0u].type().value(), relationships, - relationship_t::kAggregation); - } - else if (full_name.find("std::shared_ptr") == 0) { - found = find_relationships(args[0u].type().value(), relationships, - relationship_t::kAssociation); - } - else if (full_name.find("std::weak_ptr") == 0) { - found = find_relationships(args[0u].type().value(), relationships, - relationship_t::kAssociation); - } - else if (full_name.find("std::vector") == 0) { - if (args[0u].type().has_value()) - found = find_relationships(args[0u].type().value(), relationships, - relationship_t::kAggregation); - else - LOG_DBG("Failed to process template argument of std::vector at: {}", - fn); - } - else if (ctx.diagram().should_include(ns, name)) { + auto full_base_name = full_name.substr(0, full_name.find('<')); + + if (ctx.diagram().should_include(ns, name)) { LOG_DBG("User defined template instantiation: {} | {}", cppast::to_string(t_), cppast::to_string(t_.canonical())); @@ -1529,11 +1508,23 @@ bool translation_unit_visitor::find_relationships_in_template_instantiation( } } else { + int argument_index = 0; + auto relationship_hint = relationship_type; for (const auto &arg : args) { - if (arg.type().has_value()) { - found = find_relationships( - arg.type().value(), relationships, relationship_type); + if (ctx.config().relationship_hints().count(full_base_name) > 0) { + relationship_hint = ctx.config() + .relationship_hints() + .at(full_base_name) + .get(argument_index); } + + if (arg.type().has_value()) { + found = found || + find_relationships( + arg.type().value(), relationships, relationship_hint); + } + + argument_index++; } } @@ -1659,8 +1650,8 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( bool t_is_alias = t_unaliased_declaration != tr_declaration; // - // Here we'll hold the template base params to replace with the instantiated - // values + // Here we'll hold the template base params to replace with the + // instantiated values // std::deque> template_base_params{}; @@ -1669,8 +1660,9 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( auto tinst_full_name = cppast::to_string(t); // - // Typically, every template instantiation should have a primary_template() - // which should also be generated here if it doesn't exist yet in the model + // Typically, every template instantiation should have a + // primary_template() which should also be generated here if it doesn't + // exist yet in the model // if (t.primary_template().get(ctx.entity_index()).size()) { auto size = t.primary_template().get(ctx.entity_index()).size(); diff --git a/src/config/config.cc b/src/config/config.cc index ba1f2b30..eb0f0c1e 100644 --- a/src/config/config.cc +++ b/src/config/config.cc @@ -120,6 +120,34 @@ common::model::diagram_t include_diagram::type() const return common::model::diagram_t::kInclude; } +void class_diagram::initialize_relationship_hints() +{ + using common::model::relationship_t; + + if (!relationship_hints().count("std::vector")) { + relationship_hints().insert({"std::vector", {}}); + } + if (!relationship_hints().count("std::unique_ptr")) { + relationship_hints().insert({"std::unique_ptr", {}}); + } + if (!relationship_hints().count("std::shared_ptr")) { + relationship_hints().insert( + {"std::shared_ptr", {relationship_t::kAssociation}}); + } + if (!relationship_hints().count("std::weak_ptr")) { + relationship_hints().insert( + {"std::weak_ptr", {relationship_t::kAssociation}}); + } + if (!relationship_hints().count("std::tuple")) { + relationship_hints().insert({"std::tuple", {}}); + } + if (!relationship_hints().count("std::map")) { + relationship_hint_t hint{relationship_t::kNone}; + hint.argument_hints.insert({1, relationship_t::kAggregation}); + relationship_hints().insert({"std::tuple", std::move(hint)}); + } +} + template <> void append_value(plantuml &l, const plantuml &r) { l.append(r); @@ -141,6 +169,7 @@ using clanguml::config::layout_hint; using clanguml::config::method_arguments; using clanguml::config::package_diagram; using clanguml::config::plantuml; +using clanguml::config::relationship_hint_t; using clanguml::config::sequence_diagram; using clanguml::config::source_location; @@ -459,6 +488,9 @@ template <> struct convert { get_option(node, rhs.include_relations_also_as_members); get_option(node, rhs.generate_method_arguments); get_option(node, rhs.generate_packages); + get_option(node, rhs.relationship_hints); + + rhs.initialize_relationship_hints(); return true; } @@ -551,6 +583,42 @@ template <> struct convert { } }; +// +// relationship_hint_t Yaml decoder +// +template <> struct convert { + static bool decode(const Node &node, relationship_hint_t &rhs) + { + assert(node.Type() == NodeType::Map || node.Type() == NodeType::Scalar); + + if (node.Type() == NodeType::Scalar) { + // This will be default relationship hint for all arguments + // of this template (useful for instance for tuples) + rhs.default_hint = node.as(); + } + else { + for (const auto &it : node) { + auto key = it.first.as(); + if (key == "default") { + rhs.default_hint = node["default"].as(); + } + else { + try { + auto index = stoul(key); + rhs.argument_hints[index] = + it.second.as(); + } + catch (std::exception &e) { + return false; + } + } + } + } + + return true; + } +}; + // // config Yaml decoder // diff --git a/src/config/config.h b/src/config/config.h index c663f88d..788871d4 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -95,6 +95,27 @@ struct git_config { std::string toplevel; }; +struct relationship_hint_t { + std::map argument_hints; + common::model::relationship_t default_hint; + + relationship_hint_t(common::model::relationship_t def = + common::model::relationship_t::kAggregation) + : default_hint{def} + { + } + + common::model::relationship_t get(unsigned int argument_index) const + { + if (argument_hints.count(argument_index) > 0) + return argument_hints.at(argument_index); + + return default_hint; + } +}; + +using relationship_hints_t = std::map; + std::string to_string(const hint_t t); struct inheritable_diagram_options { @@ -113,6 +134,7 @@ struct inheritable_diagram_options { option base_directory{"__parent_path"}; option relative_to{"relative_to"}; option generate_system_headers{"generate_system_headers", false}; + option relationship_hints{"relationship_hints"}; void inherit(const inheritable_diagram_options &parent); }; @@ -139,7 +161,7 @@ struct class_diagram : public diagram { option> classes{"classes"}; option layout{"layout"}; - bool has_class(std::string clazz); + void initialize_relationship_hints(); }; struct sequence_diagram : public diagram { diff --git a/tests/test_config.cc b/tests/test_config.cc index d4316568..297ce04d 100644 --- a/tests/test_config.cc +++ b/tests/test_config.cc @@ -68,6 +68,27 @@ TEST_CASE("Test config simple", "[unit-test]") contains(diagram.include().relationships, relationship_t::kOwnership)); CHECK(contains(diagram.exclude().relationships, relationship_t::kNone)); + + CHECK(diagram.relationship_hints().at("std::vector").get(0) == + relationship_t::kComposition); + CHECK(diagram.relationship_hints().at("std::tuple").get(10) == + relationship_t::kAggregation); + CHECK(diagram.relationship_hints().at("std::map").get(0) == + relationship_t::kNone); + CHECK(diagram.relationship_hints().at("std::map").get(1) == + relationship_t::kComposition); + CHECK(diagram.relationship_hints().at("std::shared_ptr").get(0) == + relationship_t::kAssociation); + CHECK(diagram.relationship_hints().at("std::weak_ptr").get(0) == + relationship_t::kAssociation); + CHECK(diagram.relationship_hints().at("std::unique_ptr").get(0) == + relationship_t::kAggregation); + CHECK(diagram.relationship_hints().at("ns1::n2::some_template").get(0) == + relationship_t::kAssociation); + CHECK(diagram.relationship_hints().at("ns1::n2::some_template").get(2) == + relationship_t::kComposition); + CHECK(diagram.relationship_hints().at("ns1::n2::some_template").get(10) == + relationship_t::kAggregation); } TEST_CASE("Test config inherited", "[unit-test]") diff --git a/tests/test_config_data/simple.yml b/tests/test_config_data/simple.yml index d6a44955..332fb1fa 100644 --- a/tests/test_config_data/simple.yml +++ b/tests/test_config_data/simple.yml @@ -33,4 +33,14 @@ diagrams: - dependency exclude: relationships: - - none \ No newline at end of file + - none + relationship_hints: + std::vector: composition + std::map: + default: none + 1: composition + std::tuple: aggregation + ns1::n2::some_template: + default: association + 2: composition + 10: aggregation