From ffed3fef1a013b91cdc2a32fc0e0370eb8515fea Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Wed, 10 Mar 2021 11:44:16 +0100 Subject: [PATCH] Added handling of template instantiation relationships --- src/puml/class_diagram_generator.h | 4 ++ src/uml/class_diagram_visitor.h | 107 +++++++++++++++++------------ tests/t00006/test_case.h | 5 +- tests/t00009/t00009.cc | 28 +------- tests/t00009/test_case.h | 20 +++--- tests/test_cases.cc | 1 + tests/test_cases.h | 7 ++ 7 files changed, 91 insertions(+), 81 deletions(-) diff --git a/src/puml/class_diagram_generator.h b/src/puml/class_diagram_generator.h index 11e30aa6..0a51074e 100644 --- a/src/puml/class_diagram_generator.h +++ b/src/puml/class_diagram_generator.h @@ -166,6 +166,10 @@ public: destination = m_model.usr_to_name( m_config.using_namespace, r.destination); } + else if (r.destination.find("#") != std::string::npos) { + destination = m_model.usr_to_name( + m_config.using_namespace, r.destination); + } else { destination = r.destination; } diff --git a/src/uml/class_diagram_visitor.h b/src/uml/class_diagram_visitor.h index 2d27b67d..520fe920 100644 --- a/src/uml/class_diagram_visitor.h +++ b/src/uml/class_diagram_visitor.h @@ -354,12 +354,14 @@ static enum CXChildVisitResult class_visitor( case CXCursor_FieldDecl: { visit_if_cursor_valid( cursor, [ctx, &config, is_vardecl](cx::cursor cursor) { + bool added_relation_to_instantiation{false}; auto t = cursor.type(); + auto tr = t.referenced(); class_member m; m.name = cursor.spelling(); - if (t.is_template()) + if (tr.is_template()) m.type = t.unqualified(); - else if (t.is_template_parameter()) + else if (tr.is_template_parameter()) m.type = t.spelling(); else m.type = t.canonical().unqualified(); @@ -367,32 +369,32 @@ static enum CXChildVisitResult class_visitor( cursor.cxxaccess_specifier()); m.is_static = cursor.is_static(); - spdlog::info("Adding member {} {}::{} {}", m.type, - ctx->element.name, cursor.spelling(), t); + spdlog::info("Adding member {} {}::{} {}, {}", m.type, + ctx->element.name, cursor.spelling(), t, tr); - if (t.is_unexposed()) { - if (t.is_template_instantiation() && - t.type_declaration() + if (tr.is_unexposed()) { + if (tr.is_template_instantiation() && + tr.type_declaration() .specialized_cursor_template() .kind() != CXCursor_InvalidFile) { spdlog::info( "Found template instantiation: {} ..|> {}", - t.type_declaration(), - t.type_declaration() + tr.type_declaration(), + tr.type_declaration() .specialized_cursor_template()); class_ tinst; - tinst.name = t.type_declaration().spelling(); + tinst.name = tr.type_declaration().spelling(); tinst.is_template_instantiation = true; - tinst.usr = t.type_declaration().usr(); - for (int i = 0; i < t.template_arguments_count(); + tinst.usr = tr.type_declaration().usr(); + for (int i = 0; i < tr.template_arguments_count(); i++) { class_template ct; ct.type = - t.template_argument_type(i).spelling(); + tr.template_argument_type(i).spelling(); tinst.templates.emplace_back(std::move(ct)); } tinst.base_template_usr = - t.type_declaration() + tr.type_declaration() .specialized_cursor_template() .usr(); @@ -401,53 +403,68 @@ static enum CXChildVisitResult class_visitor( r.type = relationship_t::kInstantiation; r.label = ""; - tinst.relationships.emplace_back(std::move(r)); + class_relationship a; + a.destination = tinst.usr; + if (t.is_pointer() || t.is_reference()) + a.type = relationship_t::kAssociation; + else + a.type = relationship_t::kComposition; + a.label = m.name; + ctx->element.relationships.emplace_back( + std::move(a)); + tinst.relationships.emplace_back(std::move(r)); ctx->d.classes.emplace_back(std::move(tinst)); + added_relation_to_instantiation = true; } } + if (!added_relation_to_instantiation) { - relationship_t relationship_type = relationship_t::kNone; + relationship_t relationship_type = + relationship_t::kNone; - auto name = t.canonical().unqualified(); - auto destination = name; + auto name = t.canonical().unqualified(); + auto destination = name; - // Parse the field declaration to determine the relationship - // type - // Skip: - // - POD - // - function variables - spdlog::info( - "Analyzing possible relationship candidate: {}", - t.canonical().unqualified()); + // Parse the field declaration to determine the + // relationship type Skip: + // - POD + // - function variables + spdlog::info( + "Analyzing possible relationship candidate: {}", + t.canonical().unqualified()); - if (t.is_relationship() && - (config.should_include(t.canonical().unqualified()) || - t.is_template() || t.is_array())) { - std::vector> - relationships{}; - find_relationships(t, relationships); + if (t.is_relationship() && + (config.should_include( + t.canonical().unqualified()) || + t.is_template() || t.is_array())) { + std::vector> + relationships{}; + find_relationships(t, relationships); - for (const auto &[type, relationship_type] : - relationships) { - if (relationship_type != relationship_t::kNone) { - class_relationship r; - r.destination = - type.referenced().canonical().unqualified(); - r.type = relationship_type; - r.label = m.name; - ctx->element.relationships.emplace_back( - std::move(r)); + for (const auto &[type, relationship_type] : + relationships) { + if (relationship_type != + relationship_t::kNone) { + class_relationship r; + r.destination = type.referenced() + .canonical() + .unqualified(); + r.type = relationship_type; + r.label = m.name; + ctx->element.relationships.emplace_back( + std::move(r)); - spdlog::info( - "Added relationship to: {}", r.destination); + spdlog::info("Added relationship to: {}", + r.destination); + } } } } ctx->element.members.emplace_back(std::move(m)); }); - ret = CXChildVisit_Recurse; + ret = CXChildVisit_Continue; break; } case CXCursor_ClassTemplatePartialSpecialization: { diff --git a/tests/t00006/test_case.h b/tests/t00006/test_case.h index c2eb679a..a4a25a4e 100644 --- a/tests/t00006/test_case.h +++ b/tests/t00006/test_case.h @@ -64,11 +64,14 @@ TEST_CASE("Test t00006", "[unit-test]") REQUIRE_THAT(puml, IsClass(_A("NN"))); REQUIRE_THAT(puml, IsClass(_A("NNN"))); + REQUIRE_THAT(puml, + IsInstantiation(_A("custom_container"), _A("custom_container"))); + REQUIRE_THAT(puml, IsComposition(_A("R"), _A("A"), "a")); REQUIRE_THAT(puml, IsAssociation(_A("R"), _A("B"), "b")); REQUIRE_THAT(puml, IsComposition(_A("R"), _A("C"), "c")); REQUIRE_THAT(puml, IsAssociation(_A("R"), _A("D"), "d")); - REQUIRE_THAT(puml, IsComposition(_A("R"), _A("E"), "e")); + REQUIRE_THAT(puml, IsComposition(_A("R"), _A("custom_container"), "e")); REQUIRE_THAT(puml, IsComposition(_A("R"), _A("F"), "f")); REQUIRE_THAT(puml, IsAssociation(_A("R"), _A("G"), "g")); REQUIRE_THAT(puml, IsComposition(_A("R"), _A("H"), "h")); diff --git a/tests/t00009/t00009.cc b/tests/t00009/t00009.cc index 5fbd2f91..74f15079 100644 --- a/tests/t00009/t00009.cc +++ b/tests/t00009/t00009.cc @@ -1,31 +1,6 @@ #include #include -/* -@startuml - -class "A" as C_0000000046 -class C_0000000046 { -+T value -} - -class "A" as ABC -class "A" as ABCD - -class "B" as C_0000000047 -class C_0000000047 { -+A aint -+A astring -} - -C_0000000046 <|.. ABC -C_0000000046 <|.. ABCD - -ABC <-- C_0000000047 : aint -ABCD <-- C_0000000047 : astring - -@enduml -*/ namespace clanguml { namespace t00009 { @@ -37,7 +12,8 @@ public: class B { public: A aint; - A astring; + A *astring; + A> &avector; }; } } diff --git a/tests/t00009/test_case.h b/tests/t00009/test_case.h index fc659d3d..aad54071 100644 --- a/tests/t00009/test_case.h +++ b/tests/t00009/test_case.h @@ -47,18 +47,20 @@ TEST_CASE("Test t00009", "[unit-test]") REQUIRE_THAT(puml, IsClassTemplate("A", "T")); REQUIRE_THAT(puml, IsClass(_A("B"))); - /* REQUIRE_THAT(puml, IsField(Public("T value"))); - REQUIRE_THAT(puml, IsField(Public("T * pointer"))); - REQUIRE_THAT(puml, IsField(Public("T & reference"))); - REQUIRE_THAT(puml, IsField(Public("std::vector

values"))); - REQUIRE_THAT(puml, IsField(Public("std::array ints"))); - REQUIRE_THAT(puml, IsField(Public("bool (*)(int, int) comparator"))); + REQUIRE_THAT(puml, IsField(Public("A aint"))); + REQUIRE_THAT(puml, IsField(Public("A * astring"))); + REQUIRE_THAT( + puml, IsField(Public("A> & avector"))); - REQUIRE_THAT(puml, IsClassTemplate("B", "T, C<>")); + REQUIRE_THAT(puml, IsInstantiation(_A("A"), _A("A"))); + REQUIRE_THAT(puml, IsInstantiation(_A("A"), _A("A"))); + + REQUIRE_THAT(puml, IsComposition(_A("B"), _A("A"), "aint")); + REQUIRE_THAT(puml, IsAssociation(_A("B"), _A("A"), "astring")); + REQUIRE_THAT(puml, + IsAssociation(_A("B"), _A("A>"), "avector")); - REQUIRE_THAT(puml, IsField(Public("C template_template"))); - */ save_puml( "./" + config.output_directory + "/" + diagram->name + ".puml", puml); } diff --git a/tests/test_cases.cc b/tests/test_cases.cc index e9a9bae0..1eebd140 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -106,6 +106,7 @@ using clanguml::test::matchers::IsComposition; using clanguml::test::matchers::IsEnum; using clanguml::test::matchers::IsField; using clanguml::test::matchers::IsInnerClass; +using clanguml::test::matchers::IsInstantiation; using clanguml::test::matchers::Private; using clanguml::test::matchers::Protected; using clanguml::test::matchers::Public; diff --git a/tests/test_cases.h b/tests/test_cases.h index adc7c7bf..dc283d09 100644 --- a/tests/test_cases.h +++ b/tests/test_cases.h @@ -286,6 +286,13 @@ ContainsMatcher IsAggregation(std::string const &from, std::string const &to, fmt::format("{} o-- {} : {}", from, to, label), caseSensitivity)); } +ContainsMatcher IsInstantiation(std::string const &from, std::string const &to, + CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) +{ + return ContainsMatcher( + CasedString(fmt::format("{} ..|> {}", to, from), caseSensitivity)); +} + ContainsMatcher IsMethod(std::string const &name, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) {