Added handling of template instantiation relationships

This commit is contained in:
Bartek Kryza
2021-03-10 11:44:16 +01:00
parent 9cb21ab7a2
commit ffed3fef1a
7 changed files with 91 additions and 81 deletions

View File

@@ -166,6 +166,10 @@ public:
destination = m_model.usr_to_name( destination = m_model.usr_to_name(
m_config.using_namespace, r.destination); 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 { else {
destination = r.destination; destination = r.destination;
} }

View File

@@ -354,12 +354,14 @@ static enum CXChildVisitResult class_visitor(
case CXCursor_FieldDecl: { case CXCursor_FieldDecl: {
visit_if_cursor_valid( visit_if_cursor_valid(
cursor, [ctx, &config, is_vardecl](cx::cursor cursor) { cursor, [ctx, &config, is_vardecl](cx::cursor cursor) {
bool added_relation_to_instantiation{false};
auto t = cursor.type(); auto t = cursor.type();
auto tr = t.referenced();
class_member m; class_member m;
m.name = cursor.spelling(); m.name = cursor.spelling();
if (t.is_template()) if (tr.is_template())
m.type = t.unqualified(); m.type = t.unqualified();
else if (t.is_template_parameter()) else if (tr.is_template_parameter())
m.type = t.spelling(); m.type = t.spelling();
else else
m.type = t.canonical().unqualified(); m.type = t.canonical().unqualified();
@@ -367,32 +369,32 @@ static enum CXChildVisitResult class_visitor(
cursor.cxxaccess_specifier()); cursor.cxxaccess_specifier());
m.is_static = cursor.is_static(); m.is_static = cursor.is_static();
spdlog::info("Adding member {} {}::{} {}", m.type, spdlog::info("Adding member {} {}::{} {}, {}", m.type,
ctx->element.name, cursor.spelling(), t); ctx->element.name, cursor.spelling(), t, tr);
if (t.is_unexposed()) { if (tr.is_unexposed()) {
if (t.is_template_instantiation() && if (tr.is_template_instantiation() &&
t.type_declaration() tr.type_declaration()
.specialized_cursor_template() .specialized_cursor_template()
.kind() != CXCursor_InvalidFile) { .kind() != CXCursor_InvalidFile) {
spdlog::info( spdlog::info(
"Found template instantiation: {} ..|> {}", "Found template instantiation: {} ..|> {}",
t.type_declaration(), tr.type_declaration(),
t.type_declaration() tr.type_declaration()
.specialized_cursor_template()); .specialized_cursor_template());
class_ tinst; class_ tinst;
tinst.name = t.type_declaration().spelling(); tinst.name = tr.type_declaration().spelling();
tinst.is_template_instantiation = true; tinst.is_template_instantiation = true;
tinst.usr = t.type_declaration().usr(); tinst.usr = tr.type_declaration().usr();
for (int i = 0; i < t.template_arguments_count(); for (int i = 0; i < tr.template_arguments_count();
i++) { i++) {
class_template ct; class_template ct;
ct.type = ct.type =
t.template_argument_type(i).spelling(); tr.template_argument_type(i).spelling();
tinst.templates.emplace_back(std::move(ct)); tinst.templates.emplace_back(std::move(ct));
} }
tinst.base_template_usr = tinst.base_template_usr =
t.type_declaration() tr.type_declaration()
.specialized_cursor_template() .specialized_cursor_template()
.usr(); .usr();
@@ -401,53 +403,68 @@ static enum CXChildVisitResult class_visitor(
r.type = relationship_t::kInstantiation; r.type = relationship_t::kInstantiation;
r.label = ""; 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)); 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 name = t.canonical().unqualified();
auto destination = name; auto destination = name;
// Parse the field declaration to determine the relationship // Parse the field declaration to determine the
// type // relationship type Skip:
// Skip: // - POD
// - POD // - function variables
// - function variables spdlog::info(
spdlog::info( "Analyzing possible relationship candidate: {}",
"Analyzing possible relationship candidate: {}", t.canonical().unqualified());
t.canonical().unqualified());
if (t.is_relationship() && if (t.is_relationship() &&
(config.should_include(t.canonical().unqualified()) || (config.should_include(
t.is_template() || t.is_array())) { t.canonical().unqualified()) ||
std::vector<std::pair<cx::type, relationship_t>> t.is_template() || t.is_array())) {
relationships{}; std::vector<std::pair<cx::type, relationship_t>>
find_relationships(t, relationships); relationships{};
find_relationships(t, relationships);
for (const auto &[type, relationship_type] : for (const auto &[type, relationship_type] :
relationships) { relationships) {
if (relationship_type != relationship_t::kNone) { if (relationship_type !=
class_relationship r; relationship_t::kNone) {
r.destination = class_relationship r;
type.referenced().canonical().unqualified(); r.destination = type.referenced()
r.type = relationship_type; .canonical()
r.label = m.name; .unqualified();
ctx->element.relationships.emplace_back( r.type = relationship_type;
std::move(r)); r.label = m.name;
ctx->element.relationships.emplace_back(
std::move(r));
spdlog::info( spdlog::info("Added relationship to: {}",
"Added relationship to: {}", r.destination); r.destination);
}
} }
} }
} }
ctx->element.members.emplace_back(std::move(m)); ctx->element.members.emplace_back(std::move(m));
}); });
ret = CXChildVisit_Recurse; ret = CXChildVisit_Continue;
break; break;
} }
case CXCursor_ClassTemplatePartialSpecialization: { case CXCursor_ClassTemplatePartialSpecialization: {

View File

@@ -64,11 +64,14 @@ TEST_CASE("Test t00006", "[unit-test]")
REQUIRE_THAT(puml, IsClass(_A("NN"))); REQUIRE_THAT(puml, IsClass(_A("NN")));
REQUIRE_THAT(puml, IsClass(_A("NNN"))); REQUIRE_THAT(puml, IsClass(_A("NNN")));
REQUIRE_THAT(puml,
IsInstantiation(_A("custom_container<T>"), _A("custom_container<E>")));
REQUIRE_THAT(puml, IsComposition(_A("R"), _A("A"), "a")); REQUIRE_THAT(puml, IsComposition(_A("R"), _A("A"), "a"));
REQUIRE_THAT(puml, IsAssociation(_A("R"), _A("B"), "b")); REQUIRE_THAT(puml, IsAssociation(_A("R"), _A("B"), "b"));
REQUIRE_THAT(puml, IsComposition(_A("R"), _A("C"), "c")); REQUIRE_THAT(puml, IsComposition(_A("R"), _A("C"), "c"));
REQUIRE_THAT(puml, IsAssociation(_A("R"), _A("D"), "d")); 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>"), "e"));
REQUIRE_THAT(puml, IsComposition(_A("R"), _A("F"), "f")); REQUIRE_THAT(puml, IsComposition(_A("R"), _A("F"), "f"));
REQUIRE_THAT(puml, IsAssociation(_A("R"), _A("G"), "g")); REQUIRE_THAT(puml, IsAssociation(_A("R"), _A("G"), "g"));
REQUIRE_THAT(puml, IsComposition(_A("R"), _A("H"), "h")); REQUIRE_THAT(puml, IsComposition(_A("R"), _A("H"), "h"));

View File

@@ -1,31 +1,6 @@
#include <string> #include <string>
#include <vector> #include <vector>
/*
@startuml
class "A<T>" as C_0000000046
class C_0000000046 {
+T value
}
class "A<int>" as ABC
class "A<std::string>" as ABCD
class "B" as C_0000000047
class C_0000000047 {
+A<int> aint
+A<std::string> astring
}
C_0000000046 <|.. ABC
C_0000000046 <|.. ABCD
ABC <-- C_0000000047 : aint
ABCD <-- C_0000000047 : astring
@enduml
*/
namespace clanguml { namespace clanguml {
namespace t00009 { namespace t00009 {
@@ -37,7 +12,8 @@ public:
class B { class B {
public: public:
A<int> aint; A<int> aint;
A<std::string> astring; A<std::string> *astring;
A<std::vector<std::string>> &avector;
}; };
} }
} }

View File

@@ -47,18 +47,20 @@ TEST_CASE("Test t00009", "[unit-test]")
REQUIRE_THAT(puml, IsClassTemplate("A", "T")); REQUIRE_THAT(puml, IsClassTemplate("A", "T"));
REQUIRE_THAT(puml, IsClass(_A("B"))); REQUIRE_THAT(puml, IsClass(_A("B")));
/*
REQUIRE_THAT(puml, IsField(Public("T value"))); REQUIRE_THAT(puml, IsField(Public("T value")));
REQUIRE_THAT(puml, IsField(Public("T * pointer"))); REQUIRE_THAT(puml, IsField(Public("A<int> aint")));
REQUIRE_THAT(puml, IsField(Public("T & reference"))); REQUIRE_THAT(puml, IsField(Public("A<std::string> * astring")));
REQUIRE_THAT(puml, IsField(Public("std::vector<P> values"))); REQUIRE_THAT(
REQUIRE_THAT(puml, IsField(Public("std::array<int, N> ints"))); puml, IsField(Public("A<std::vector<std::string>> & avector")));
REQUIRE_THAT(puml, IsField(Public("bool (*)(int, int) comparator")));
REQUIRE_THAT(puml, IsClassTemplate("B", "T, C<>")); REQUIRE_THAT(puml, IsInstantiation(_A("A<T>"), _A("A<int>")));
REQUIRE_THAT(puml, IsInstantiation(_A("A<T>"), _A("A<std::string>")));
REQUIRE_THAT(puml, IsComposition(_A("B"), _A("A<int>"), "aint"));
REQUIRE_THAT(puml, IsAssociation(_A("B"), _A("A<std::string>"), "astring"));
REQUIRE_THAT(puml,
IsAssociation(_A("B"), _A("A<std::vector<std::string>>"), "avector"));
REQUIRE_THAT(puml, IsField(Public("C<T> template_template")));
*/
save_puml( save_puml(
"./" + config.output_directory + "/" + diagram->name + ".puml", puml); "./" + config.output_directory + "/" + diagram->name + ".puml", puml);
} }

View File

@@ -106,6 +106,7 @@ using clanguml::test::matchers::IsComposition;
using clanguml::test::matchers::IsEnum; using clanguml::test::matchers::IsEnum;
using clanguml::test::matchers::IsField; using clanguml::test::matchers::IsField;
using clanguml::test::matchers::IsInnerClass; using clanguml::test::matchers::IsInnerClass;
using clanguml::test::matchers::IsInstantiation;
using clanguml::test::matchers::Private; using clanguml::test::matchers::Private;
using clanguml::test::matchers::Protected; using clanguml::test::matchers::Protected;
using clanguml::test::matchers::Public; using clanguml::test::matchers::Public;

View File

@@ -286,6 +286,13 @@ ContainsMatcher IsAggregation(std::string const &from, std::string const &to,
fmt::format("{} o-- {} : {}", from, to, label), caseSensitivity)); 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, ContainsMatcher IsMethod(std::string const &name,
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
{ {