Fixed basic template instantiation relationships

This commit is contained in:
Bartek Kryza
2021-03-25 22:30:21 +01:00
parent 2c2040d95d
commit a8bab3931e
9 changed files with 179 additions and 25 deletions

View File

@@ -91,6 +91,18 @@ std::string fully_prefixed(const cppast::cpp_entity &e)
return fmt::format("{}", fmt::join(res.rbegin(), res.rend(), "::")); return fmt::format("{}", fmt::join(res.rbegin(), res.rend(), "::"));
} }
const cppast::cpp_type &unreferenced(const cppast::cpp_type &t)
{
if (t.kind() == cppast::cpp_type_kind::pointer_t)
return unreferenced(
static_cast<const cppast::cpp_pointer_type &>(t).pointee());
else if (t.kind() == cppast::cpp_type_kind::reference_t)
return unreferenced(
static_cast<const cppast::cpp_reference_type &>(t).referee());
return t;
}
} // namespace util } // namespace util
} // namespace cx } // namespace cx
} // namespace clanguml } // namespace clanguml

View File

@@ -20,6 +20,7 @@
#include <clang-c/CXCompilationDatabase.h> #include <clang-c/CXCompilationDatabase.h>
#include <clang-c/Index.h> #include <clang-c/Index.h>
#include <cppast/cpp_entity.hpp> #include <cppast/cpp_entity.hpp>
#include <cppast/cpp_type.hpp>
#include <string> #include <string>
@@ -43,6 +44,8 @@ std::string full_name(const cppast::cpp_entity &e);
std::string fully_prefixed(const cppast::cpp_entity &e); std::string fully_prefixed(const cppast::cpp_entity &e);
const cppast::cpp_type& unreferenced(const cppast::cpp_type &t);
} // namespace util } // namespace util
} // namespace cx } // namespace cx
} // namespace clanguml } // namespace clanguml

View File

@@ -318,7 +318,7 @@ clanguml::model::class_diagram::diagram generate(
type_safe::ref(idx)}; type_safe::ref(idx)};
// Process all matching translation units // Process all matching translation units
clanguml::visitor::class_diagram::tu_visitor ctx(d, diagram); clanguml::visitor::class_diagram::tu_visitor ctx(idx, d, diagram);
cppast::parse_files(parser, translation_units, db); cppast::parse_files(parser, translation_units, db);
for (auto &file : parser.files()) for (auto &file : parser.files())
ctx(file); ctx(file);

View File

@@ -22,6 +22,36 @@ namespace clanguml {
namespace model { namespace model {
namespace class_diagram { namespace class_diagram {
std::atomic_uint64_t element::m_nextId = 1; std::atomic_uint64_t element::m_nextId = 1;
std::string to_string(relationship_t r)
{
switch (r) {
case relationship_t::kNone:
return "none";
case relationship_t::kExtension:
return "extension";
case relationship_t::kComposition:
return "composition";
case relationship_t::kAggregation:
return "aggregation";
case relationship_t::kContainment:
return "containment";
case relationship_t::kOwnership:
return "ownership";
case relationship_t::kAssociation:
return "association";
case relationship_t::kInstantiation:
return "instantiation";
case relationship_t::kFriendship:
return "frendship";
case relationship_t::kDependency:
return "dependency";
default:
return "invalid";
}
}
} }
} }
} }

View File

@@ -49,6 +49,8 @@ enum class relationship_t {
kDependency kDependency
}; };
std::string to_string(relationship_t r);
class element { class element {
public: public:
element() element()
@@ -226,7 +228,7 @@ struct diagram {
void add_class(class_ &&c) void add_class(class_ &&c)
{ {
spdlog::debug("ADDING CLASS: {}, {}", c.name, c.usr); spdlog::debug("Adding class: {}, {}", c.name, c.usr);
auto it = std::find(classes.begin(), classes.end(), c); auto it = std::find(classes.begin(), classes.end(), c);
if (it == classes.end()) if (it == classes.end())
classes.emplace_back(std::move(c)); classes.emplace_back(std::move(c));

View File

@@ -18,6 +18,7 @@
#include "class_diagram_visitor.h" #include "class_diagram_visitor.h"
#include <cppast/cpp_array_type.hpp> #include <cppast/cpp_array_type.hpp>
#include <cppast/cpp_class_template.hpp>
#include <cppast/cpp_entity_kind.hpp> #include <cppast/cpp_entity_kind.hpp>
#include <cppast/cpp_enum.hpp> #include <cppast/cpp_enum.hpp>
#include <cppast/cpp_member_function.hpp> #include <cppast/cpp_member_function.hpp>
@@ -123,7 +124,6 @@ void tu_visitor::process_enum_declaration(const cppast::cpp_enum &enm)
void tu_visitor::process_class_declaration(const cppast::cpp_class &cls) void tu_visitor::process_class_declaration(const cppast::cpp_class &cls)
{ {
class_ c; class_ c;
c.usr = cx::util::full_name(cls);
c.is_struct = cls.class_kind() == cppast::cpp_class_kind::struct_t; c.is_struct = cls.class_kind() == cppast::cpp_class_kind::struct_t;
c.name = cx::util::full_name(cls); c.name = cx::util::full_name(cls);
@@ -230,6 +230,8 @@ void tu_visitor::process_class_declaration(const cppast::cpp_class &cls)
break; break;
} }
} }
cls.set_user_data(strdup(c.full_name(ctx.config.using_namespace).c_str()));
c.usr = c.full_name(ctx.config.using_namespace);
ctx.d.add_class(std::move(c)); ctx.d.add_class(std::move(c));
} }
@@ -243,6 +245,52 @@ void tu_visitor::process_field(const cppast::cpp_member_variable &mv, class_ &c,
m.scope = detail::cpp_access_specifier_to_scope(as); m.scope = detail::cpp_access_specifier_to_scope(as);
m.is_static = false; m.is_static = false;
const auto &tr = cx::util::unreferenced(mv.type());
if (tr.kind() == cppast::cpp_type_kind::template_instantiation_t) {
const auto &template_instantiation_type =
static_cast<const cppast::cpp_template_instantiation_type &>(tr);
if (template_instantiation_type.primary_template()
.get(ctx.entity_index)
.size()) {
// Here we need the name of the primary template with full namespace
// prefix to apply config inclusion filters
auto primary_template_name = cx::util::full_name(
template_instantiation_type.primary_template()
.get(ctx.entity_index)[0]
.get());
spdlog::debug(
"MAYBE BUILDING INSTANTIATION FOR: {}", primary_template_name);
if (ctx.config.should_include(primary_template_name)) {
class_ tinst = build_template_instantiation(
mv, template_instantiation_type);
class_relationship r;
r.destination = tinst.base_template_usr;
r.type = relationship_t::kInstantiation;
r.label = "";
tinst.add_relationship(std::move(r));
class_relationship rr;
rr.destination = tinst.usr;
if (mv.type().kind() == cppast::cpp_type_kind::pointer_t ||
mv.type().kind() == cppast::cpp_type_kind::reference_t)
rr.type = relationship_t::kAssociation;
else
rr.type = relationship_t::kComposition;
rr.label = mv.name();
spdlog::debug(
"Adding field instantiation relationship {} {} {} : {}",
rr.destination, model::class_diagram::to_string(rr.type),
c.usr, rr.label);
c.add_relationship(std::move(rr));
ctx.d.add_class(std::move(tinst));
}
}
}
if (mv.type().kind() != cppast::cpp_type_kind::builtin_t) { if (mv.type().kind() != cppast::cpp_type_kind::builtin_t) {
std::vector<std::pair<std::string, relationship_t>> relationships; std::vector<std::pair<std::string, relationship_t>> relationships;
@@ -254,9 +302,12 @@ void tu_visitor::process_field(const cppast::cpp_member_variable &mv, class_ &c,
r.destination = type; r.destination = type;
r.type = relationship_type; r.type = relationship_type;
r.label = m.name; r.label = m.name;
c.relationships.emplace_back(std::move(r));
spdlog::debug("Added relationship to: {}", r.destination); spdlog::debug("Adding field relationship {} {} {} : {}",
r.destination, model::class_diagram::to_string(r.type),
c.usr, r.label);
c.relationships.emplace_back(std::move(r));
} }
} }
} }
@@ -419,8 +470,10 @@ void tu_visitor::find_relationships(const cppast::cpp_type &t_,
std::vector<std::pair<std::string, relationship_t>> &relationships, std::vector<std::pair<std::string, relationship_t>> &relationships,
relationship_t relationship_hint) relationship_t relationship_hint)
{ {
spdlog::debug("Finding relationships for type {}", cppast::to_string(t_));
relationship_t relationship_type = relationship_hint; relationship_t relationship_type = relationship_hint;
const auto &t = cppast::remove_cv(t_); const auto &t = cppast::remove_cv(cx::util::unreferenced(t_));
if (t.kind() == cppast::cpp_type_kind::array_t) { if (t.kind() == cppast::cpp_type_kind::array_t) {
auto &a = static_cast<const cppast::cpp_array_type &>(t); auto &a = static_cast<const cppast::cpp_array_type &>(t);
@@ -460,7 +513,7 @@ void tu_visitor::find_relationships(const cppast::cpp_type &t_,
} }
else if (name.find("std::vector") == 0) { else if (name.find("std::vector") == 0) {
find_relationships(args[0u].type().value(), relationships, find_relationships(args[0u].type().value(), relationships,
relationship_t::kAggregation); relationship_t::kComposition);
} }
else { else {
for (const auto &arg : args) { for (const auto &arg : args) {
@@ -469,26 +522,72 @@ void tu_visitor::find_relationships(const cppast::cpp_type &t_,
} }
} }
} }
else if (t.kind() == cppast::cpp_type_kind::user_defined_t) { else if (t_.kind() == cppast::cpp_type_kind::pointer_t) {
if (relationship_type != relationship_t::kNone) auto &p = static_cast<const cppast::cpp_pointer_type &>(t_);
relationships.emplace_back(cppast::to_string(t), relationship_hint); spdlog::debug("TEST2");
else
relationships.emplace_back(
cppast::to_string(t), relationship_t::kComposition);
}
else if (t.kind() == cppast::cpp_type_kind::pointer_t) {
auto &p = static_cast<const cppast::cpp_pointer_type &>(t);
find_relationships( find_relationships(
p.pointee(), relationships, relationship_t::kAssociation); p.pointee(), relationships, relationship_t::kAssociation);
} }
else if (t.kind() == cppast::cpp_type_kind::reference_t) { else if (t_.kind() == cppast::cpp_type_kind::reference_t) {
auto &r = static_cast<const cppast::cpp_reference_type &>(t); spdlog::debug("TEST3");
auto &r = static_cast<const cppast::cpp_reference_type &>(t_);
auto rt = relationship_t::kAssociation; auto rt = relationship_t::kAssociation;
if (r.reference_kind() == cppast::cpp_reference::cpp_ref_rvalue) { if (r.reference_kind() == cppast::cpp_reference::cpp_ref_rvalue) {
rt = relationship_t::kComposition; rt = relationship_t::kComposition;
} }
find_relationships(r.referee(), relationships, rt); find_relationships(r.referee(), relationships, rt);
} }
else if (cppast::remove_cv(t_).kind() ==
cppast::cpp_type_kind::user_defined_t) {
if (ctx.config.should_include(cppast::to_string(t_.canonical())))
if (relationship_type != relationship_t::kNone)
relationships.emplace_back(
cppast::to_string(cppast::remove_cv(t_)), relationship_type);
else
relationships.emplace_back(
cppast::to_string(cppast::remove_cv(t_)), relationship_t::kComposition);
}
}
class_ tu_visitor::build_template_instantiation(const cppast::cpp_entity &e,
const cppast::cpp_template_instantiation_type &t)
{
spdlog::debug("Found template instantiation: {} ..|> {}",
cppast::to_string(t.canonical()), t.primary_template().name());
class_ tinst;
const auto &primary_template_ref =
static_cast<const cppast::cpp_class_template &>(
t.primary_template().get(ctx.entity_index)[0].get())
.class_();
tinst.name = primary_template_ref.name();
if (primary_template_ref.user_data())
tinst.base_template_usr =
static_cast<const char *>(primary_template_ref.user_data());
tinst.is_template_instantiation = true;
for (const auto &targ : t.arguments().value()) {
class_template ct;
if (targ.type()) {
ct.type = cppast::to_string(targ.type().value());
}
else if (targ.expression()) {
const auto &exp = targ.expression().value();
if (exp.kind() == cppast::cpp_expression_kind::literal_t)
ct.type =
static_cast<const cppast::cpp_literal_expression &>(exp)
.value();
}
spdlog::debug("Adding template argument '{}'", ct.type);
tinst.templates.emplace_back(std::move(ct));
}
tinst.usr = tinst.full_name(ctx.config.using_namespace);
return tinst;
} }
// //

View File

@@ -38,14 +38,17 @@ namespace visitor {
namespace class_diagram { namespace class_diagram {
struct tu_context { struct tu_context {
tu_context(clanguml::model::class_diagram::diagram &d_, tu_context(cppast::cpp_entity_index &idx,
clanguml::model::class_diagram::diagram &d_,
const clanguml::config::class_diagram &config_) const clanguml::config::class_diagram &config_)
: d{d_} : entity_index{idx}
, d{d_}
, config{config_} , config{config_}
{ {
} }
std::vector<std::string> namespace_; std::vector<std::string> namespace_;
cppast::cpp_entity_index &entity_index;
clanguml::model::class_diagram::diagram &d; clanguml::model::class_diagram::diagram &d;
const clanguml::config::class_diagram &config; const clanguml::config::class_diagram &config;
}; };
@@ -65,9 +68,10 @@ template <typename T> struct element_visitor_context {
class tu_visitor { class tu_visitor {
public: public:
tu_visitor(clanguml::model::class_diagram::diagram &d_, tu_visitor(cppast::cpp_entity_index &idx_,
clanguml::model::class_diagram::diagram &d_,
const clanguml::config::class_diagram &config_) const clanguml::config::class_diagram &config_)
: ctx{d_, config_} : ctx{idx_, d_, config_}
{ {
} }
@@ -123,6 +127,10 @@ public:
clanguml::model::class_diagram::class_ &parent); clanguml::model::class_diagram::class_ &parent);
private: private:
clanguml::model::class_diagram::class_ build_template_instantiation(
const cppast::cpp_entity &e,
const cppast::cpp_template_instantiation_type &t);
tu_context ctx; tu_context ctx;
}; };

View File

@@ -47,9 +47,9 @@ TEST_CASE("t00009", "[test-case][class]")
REQUIRE_THAT(puml, IsField(Public("T value"))); REQUIRE_THAT(puml, IsField(Public("T value")));
REQUIRE_THAT(puml, IsField(Public("A<int> aint"))); REQUIRE_THAT(puml, IsField(Public("A<int> aint")));
REQUIRE_THAT(puml, IsField(Public("A<std::string> * astring"))); REQUIRE_THAT(puml, IsField(Public("A<std::string>* astring")));
REQUIRE_THAT( REQUIRE_THAT(
puml, IsField(Public("A<std::vector<std::string>> & avector"))); puml, IsField(Public("A<std::vector<std::string>>& avector")));
REQUIRE_THAT(puml, IsInstantiation(_A("A<T>"), _A("A<int>"))); REQUIRE_THAT(puml, IsInstantiation(_A("A<T>"), _A("A<int>")));
REQUIRE_THAT(puml, IsInstantiation(_A("A<T>"), _A("A<std::string>"))); REQUIRE_THAT(puml, IsInstantiation(_A("A<T>"), _A("A<std::string>")));

View File

@@ -45,7 +45,7 @@ TEST_CASE("t00010", "[test-case][class]")
REQUIRE_THAT(puml, IsClassTemplate("A", "T, P")); REQUIRE_THAT(puml, IsClassTemplate("A", "T, P"));
REQUIRE_THAT(puml, IsClassTemplate("B", "T")); REQUIRE_THAT(puml, IsClassTemplate("B", "T"));
REQUIRE_THAT(puml, IsField(Public("A<T, std::string> astring"))); REQUIRE_THAT(puml, IsField(Public("A<T,std::string> astring")));
REQUIRE_THAT(puml, IsField(Public("B<int> aintstring"))); REQUIRE_THAT(puml, IsField(Public("B<int> aintstring")));
REQUIRE_THAT(puml, IsInstantiation(_A("A<T, P>"), _A("A<T, std::string>"))); REQUIRE_THAT(puml, IsInstantiation(_A("A<T, P>"), _A("A<T, std::string>")));