Fixed dependency generation for template specializations

This commit is contained in:
Bartek Kryza
2021-03-20 19:54:04 +01:00
parent 79ad29164c
commit 90952d8c3c
9 changed files with 91 additions and 29 deletions

View File

@@ -17,6 +17,8 @@
*/
#pragma once
#include "util/util.h"
#include <spdlog/spdlog.h>
#include <yaml-cpp/yaml.h>
@@ -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;
}
};

View File

@@ -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;
}
}
}

View File

@@ -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<std::string> 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:

View File

@@ -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(
}
}
}

View File

@@ -17,6 +17,8 @@
*/
#include "util.h"
#include <spdlog/spdlog.h>
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<std::string> 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, " "));
}
}
}

View File

@@ -60,5 +60,14 @@ std::vector<std::string> split(std::string str, std::string delimiter);
*/
std::string ns_relative(
const std::vector<std::string> &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);
}
}

View File

@@ -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"))));

View File

@@ -47,9 +47,12 @@ public:
int get_d2(D &&d) { return d.d; }
template <typename T> T get_e(E<T> &e) { return e.e; }
int get_int_e(E<int> &e) { return e.e; }
int get_int_e(const E<int> &e) { return e.e; }
template <typename T> T get_f(const F<T> &f) { return f.f; }
private:
mutable E<std::string> estring;
};
}
}

View File

@@ -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<T>")));
REQUIRE_THAT(puml, IsDependency(_A("R"), _A("E<int>")));
REQUIRE_THAT(puml, IsInstantiation(_A("E<T>"), _A("E<int>")));
REQUIRE_THAT(puml, IsInstantiation(_A("E<T>"), _A("E<std::string>")));
REQUIRE_THAT(puml, IsComposition(_A("R"), _A("E<std::string>"), "estring"));
REQUIRE_THAT(puml, IsDependency(_A("R"), _A("ABCD::F<T>")));
REQUIRE_THAT(puml, IsDependency(_A("D"), _A("R")));