diff --git a/src/config/config.h b/src/config/config.h index 29b01740..87ca1506 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -17,6 +17,8 @@ */ #pragma once +#include "util/util.h" + #include #include @@ -51,8 +53,10 @@ struct diagram { plantuml puml; - bool should_include(const std::string &name) const + bool should_include(const std::string &name_) const { + auto name = clanguml::util::unqualify(name_); + for (const auto &ex : exclude.namespaces) { if (name.find(ex) == 0) return false; @@ -68,6 +72,8 @@ struct diagram { return true; } + spdlog::debug("Skipping from diagram: {}", name); + return false; } }; diff --git a/src/cx/type.cc b/src/cx/type.cc index 39499f01..21a522a7 100644 --- a/src/cx/type.cc +++ b/src/cx/type.cc @@ -38,5 +38,13 @@ std::string type::instantiation_template() const return cur.fully_qualified(); } + +bool type::is_template_instantiation() const +{ + auto s = spelling(); + auto it = s.find('<'); + return it != std::string::npos && + referenced().type_declaration().kind() != CXCursor_ClassTemplate; +} } } diff --git a/src/cx/type.h b/src/cx/type.h index 4abf2276..24affbe6 100644 --- a/src/cx/type.h +++ b/src/cx/type.h @@ -205,12 +205,7 @@ public: return clang_Type_getCXXRefQualifier(m_type); } - bool is_template_instantiation() const - { - auto s = spelling(); - auto it = s.find('<'); - return it != std::string::npos; - } + bool is_template_instantiation() const; std::string instantiation_template() const; @@ -221,18 +216,7 @@ public: */ std::string unqualified() const { - auto toks = clanguml::util::split(spelling(), " "); - const std::vector qualifiers = { - "static", "const", "volatile", "register", "mutable"}; - - toks.erase(toks.begin(), - std::find_if( - toks.begin(), toks.end(), [&qualifiers](const auto &t) { - return std::count( - qualifiers.begin(), qualifiers.end(), t) == 0; - })); - - return fmt::format("{}", fmt::join(toks, " ")); + return clanguml::util::unqualify(spelling()); } private: diff --git a/src/uml/class_diagram_visitor.cc b/src/uml/class_diagram_visitor.cc index db028ac7..98d01069 100644 --- a/src/uml/class_diagram_visitor.cc +++ b/src/uml/class_diagram_visitor.cc @@ -183,7 +183,8 @@ enum CXChildVisitResult method_parameter_visitor( switch (cursor.kind()) { case CXCursor_ParmDecl: { spdlog::debug("Analyzing method parameter: {}, {}, {}", cursor, - cursor.type(), cursor.type().named_type()); + cursor.type().referenced(), + cursor.type().referenced().type_declaration()); auto t = cursor.type(); method_parameter mp; @@ -198,6 +199,10 @@ enum CXChildVisitResult method_parameter_visitor( if (t.is_template_instantiation()) { rdestination = t.referenced().instantiation_template(); } + else if (t.spelling().find('<') != std::string::npos) { + rdestination = + t.referenced().type_declaration().fully_qualified(); + } else { rdestination = t.referenced().spelling(); } @@ -205,18 +210,43 @@ enum CXChildVisitResult method_parameter_visitor( if (ctx->ctx->config.should_include(rdestination) && rdestination != ctx->parent_class->name) { - spdlog::debug("ADDING DEPENDENCY TO {} \n\tCURSOR={} " + spdlog::debug("Adding dependency to {} \n\tCURSOR={} " "\n\tREFTYPE={} \n\tTYPEDECL={}", - rdestination, cursor, t.referenced(), - t.referenced().type_declaration().usr()); + t.referenced().spelling(), cursor, t.referenced(), + t.referenced().type_declaration()); class_relationship r; r.type = relationship_t::kDependency; - r.destination = t.referenced().type_declaration().usr(); + + if (t.referenced().is_template_instantiation() && + (t.referenced().type_declaration().kind() != + CXCursor_InvalidFile || + t.referenced() + .type_declaration() + .specialized_cursor_template() + .kind() != CXCursor_InvalidFile)) { + class_ tinst = build_template_instantiation( + cursor, t.referenced()); + + class_relationship ri; + ri.destination = tinst.base_template_usr; + ri.type = relationship_t::kInstantiation; + ri.label = ""; + + tinst.add_relationship(std::move(ri)); + + r.destination = t.referenced().unqualified(); + + ctx->d.classes.emplace_back(std::move(tinst)); + } + else + r.destination = t.referenced().type_declaration().usr(); assert(ctx->parent_class != nullptr); - ctx->parent_class->add_relationship(std::move(r)); + if ((r.destination != ctx->parent_class->name) && + (r.destination != ctx->parent_class->usr)) + ctx->parent_class->add_relationship(std::move(r)); } ret = CXChildVisit_Continue; @@ -500,7 +530,7 @@ bool process_template_specialization_class_field( cx::cursor cursor, cx::type t, class_ *parent, struct tu_context *ctx) { auto tr = t.referenced(); - if (tr.is_template_instantiation() && + if (tr.spelling().find('<') != std::string::npos && (tr.type_declaration().kind() != CXCursor_InvalidFile || tr.type_declaration().specialized_cursor_template().kind() != CXCursor_InvalidFile)) { @@ -537,7 +567,7 @@ bool process_template_specialization_class_field( parent->relationships.emplace_back(std::move(a)); - tinst.relationships.emplace_back(std::move(r)); + tinst.add_relationship(std::move(r)); ctx->d.classes.emplace_back(std::move(tinst)); return true; @@ -756,4 +786,3 @@ enum CXChildVisitResult translation_unit_visitor( } } } - diff --git a/src/util/util.cc b/src/util/util.cc index a992797c..3bb9cf8b 100644 --- a/src/util/util.cc +++ b/src/util/util.cc @@ -17,6 +17,8 @@ */ #include "util.h" +#include + namespace clanguml { namespace util { @@ -85,5 +87,19 @@ std::string ns_relative( } return res; } + +std::string unqualify(const std::string &s) +{ + auto toks = clanguml::util::split(s, " "); + const std::vector qualifiers = { + "static", "const", "volatile", "register", "mutable", "struct", "enum"}; + + toks.erase(toks.begin(), + std::find_if(toks.begin(), toks.end(), [&qualifiers](const auto &t) { + return std::count(qualifiers.begin(), qualifiers.end(), t) == 0; + })); + + return fmt::format("{}", fmt::join(toks, " ")); +} } } diff --git a/src/util/util.h b/src/util/util.h index 4677e71f..b16acf56 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -60,5 +60,14 @@ std::vector split(std::string str, std::string delimiter); */ std::string ns_relative( const std::vector &namespaces, const std::string &n); + +/** + * @brief Remove any qualifiers (e.g. const) from type. + * + * @param s String spelling of the type. + * + * @return Unqualified type spelling. + */ +std::string unqualify(const std::string &s); } } diff --git a/tests/t00003/test_case.h b/tests/t00003/test_case.h index e3b9285c..93a44792 100644 --- a/tests/t00003/test_case.h +++ b/tests/t00003/test_case.h @@ -43,6 +43,8 @@ TEST_CASE("t00003", "[test-case][class]") REQUIRE_THAT(puml, EndsWith("@enduml\n")); REQUIRE_THAT(puml, IsClass(_A("A"))); + REQUIRE_THAT(puml, !IsDependency(_A("A"), _A("A"))); + REQUIRE_THAT(puml, IsMethod(Default(Public("A")))); REQUIRE_THAT(puml, IsMethod(Default(Public("~A")))); diff --git a/tests/t00013/t00013.cc b/tests/t00013/t00013.cc index a6ead09f..cb5821e0 100644 --- a/tests/t00013/t00013.cc +++ b/tests/t00013/t00013.cc @@ -47,9 +47,12 @@ public: int get_d2(D &&d) { return d.d; } template T get_e(E &e) { return e.e; } - int get_int_e(E &e) { return e.e; } + int get_int_e(const E &e) { return e.e; } template T get_f(const F &f) { return f.f; } + +private: + mutable E estring; }; } } diff --git a/tests/t00013/test_case.h b/tests/t00013/test_case.h index 8eec8546..ba41ea46 100644 --- a/tests/t00013/test_case.h +++ b/tests/t00013/test_case.h @@ -46,11 +46,16 @@ TEST_CASE("t00013", "[test-case][class]") REQUIRE_THAT(puml, IsClass(_A("B"))); REQUIRE_THAT(puml, IsClass(_A("C"))); REQUIRE_THAT(puml, IsClass(_A("D"))); + REQUIRE_THAT(puml, !IsDependency(_A("R"), _A("R"))); REQUIRE_THAT(puml, IsDependency(_A("R"), _A("A"))); REQUIRE_THAT(puml, IsDependency(_A("R"), _A("B"))); REQUIRE_THAT(puml, IsDependency(_A("R"), _A("C"))); REQUIRE_THAT(puml, IsDependency(_A("R"), _A("D"))); REQUIRE_THAT(puml, IsDependency(_A("R"), _A("E"))); + REQUIRE_THAT(puml, IsDependency(_A("R"), _A("E"))); + REQUIRE_THAT(puml, IsInstantiation(_A("E"), _A("E"))); + REQUIRE_THAT(puml, IsInstantiation(_A("E"), _A("E"))); + REQUIRE_THAT(puml, IsComposition(_A("R"), _A("E"), "estring")); REQUIRE_THAT(puml, IsDependency(_A("R"), _A("ABCD::F"))); REQUIRE_THAT(puml, IsDependency(_A("D"), _A("R")));