Fixed rendering of member variables with alias to template or alias template (t00014)
This commit is contained in:
@@ -89,7 +89,7 @@ void generator::generate(const class_ &c, std::ostream &ostr) const
|
||||
return mp.to_string(m_config.using_namespace());
|
||||
});
|
||||
auto args_string = fmt::format("{}", fmt::join(params, ", "));
|
||||
if (m_config.generate_method_arguments() !=
|
||||
if (m_config.generate_method_arguments() ==
|
||||
config::method_arguments::abbreviated) {
|
||||
args_string = clanguml::util::abbreviate(args_string, 10);
|
||||
}
|
||||
@@ -277,21 +277,21 @@ void generator::generate(std::ostream &ostr) const
|
||||
|
||||
if (m_config.should_include_entities("classes")) {
|
||||
for (const auto &c : m_model.classes()) {
|
||||
if (!m_config.should_include(c.get().name()))
|
||||
if (!m_config.should_include(c->get_namespace(), c->name()))
|
||||
continue;
|
||||
generate_alias(*c, ostr);
|
||||
ostr << '\n';
|
||||
}
|
||||
|
||||
for (const auto &e : m_model.enums()) {
|
||||
if (!m_config.should_include(e.get().name()))
|
||||
if (!m_config.should_include(e->get_namespace(), e->name()))
|
||||
continue;
|
||||
generate_alias(*e, ostr);
|
||||
ostr << '\n';
|
||||
}
|
||||
|
||||
for (const auto &c : m_model.classes()) {
|
||||
if (!m_config.should_include(c.get().name()))
|
||||
if (!m_config.should_include(c->get_namespace(), c->name()))
|
||||
continue;
|
||||
generate(*c, ostr);
|
||||
ostr << '\n';
|
||||
|
||||
@@ -101,10 +101,10 @@ std::string class_::full_name(bool relative) const
|
||||
using namespace clanguml::util;
|
||||
|
||||
std::ostringstream ostr;
|
||||
if (relative)
|
||||
if (relative && starts_with(get_namespace(), using_namespaces()))
|
||||
ostr << ns_relative(using_namespaces(), name());
|
||||
else
|
||||
ostr << name();
|
||||
ostr << name_and_ns();
|
||||
|
||||
if (!templates_.empty()) {
|
||||
std::vector<std::string> tnames;
|
||||
|
||||
@@ -37,9 +37,9 @@ public:
|
||||
class_(const std::vector<std::string> &using_namespaces);
|
||||
|
||||
class_(const class_ &) = delete;
|
||||
class_(class_ &&) = default;
|
||||
class_(class_ &&) noexcept = delete;
|
||||
class_ &operator=(const class_ &) = delete;
|
||||
class_ &operator=(class_ &&) = default;
|
||||
class_ &operator=(class_ &&) = delete;
|
||||
|
||||
bool is_struct() const;
|
||||
void is_struct(bool is_struct);
|
||||
|
||||
@@ -21,14 +21,17 @@
|
||||
#include "util/error.h"
|
||||
#include "util/util.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
namespace clanguml::class_diagram::model {
|
||||
|
||||
const std::vector<type_safe::object_ref<class_>> diagram::classes() const
|
||||
const std::vector<type_safe::object_ref<const class_>> diagram::classes() const
|
||||
{
|
||||
return classes_;
|
||||
}
|
||||
|
||||
const std::vector<type_safe::object_ref<enum_>> diagram::enums() const
|
||||
const std::vector<type_safe::object_ref<const enum_>> diagram::enums() const
|
||||
{
|
||||
return enums_;
|
||||
}
|
||||
@@ -57,16 +60,32 @@ void diagram::add_package(std::unique_ptr<common::model::package> &&p)
|
||||
{
|
||||
LOG_DBG("Adding namespace package: {}, {}", p->name(), p->full_name(true));
|
||||
|
||||
add_element(p->get_namespace(), std::move(p));
|
||||
auto ns = p->get_namespace();
|
||||
add_element(ns, std::move(p));
|
||||
}
|
||||
|
||||
void diagram::add_class(std::unique_ptr<class_> &&c)
|
||||
{
|
||||
LOG_DBG("Adding class: {}, {}", c->name(), c->full_name());
|
||||
|
||||
if (util::contains(c->name(), "::"))
|
||||
throw std::runtime_error("Name cannot contain namespace: " + c->name());
|
||||
|
||||
if (util::contains(c->name(), "<"))
|
||||
throw std::runtime_error("Name cannot contain <: " + c->name());
|
||||
|
||||
if (util::contains(c->name(), "*"))
|
||||
throw std::runtime_error("Name cannot contain *: " + c->name());
|
||||
|
||||
if (!has_class(*c)) {
|
||||
LOG_DBG("### ADDED CLASS WITH ADDRESS: {}", (void *)c.get());
|
||||
classes_.emplace_back(*c);
|
||||
add_element(c->get_namespace(), std::move(c));
|
||||
auto ns = c->get_relative_namespace();
|
||||
auto name = c->name();
|
||||
add_element(ns, std::move(c));
|
||||
ns.push_back(name);
|
||||
const auto ccc = get_element<class_>(ns);
|
||||
assert(ccc.value().name() == name);
|
||||
}
|
||||
else
|
||||
LOG_DBG(
|
||||
@@ -77,9 +96,12 @@ void diagram::add_enum(std::unique_ptr<enum_> &&e)
|
||||
{
|
||||
LOG_DBG("Adding enum: {}", e->name());
|
||||
|
||||
assert(!util::contains(e->name(), "::"));
|
||||
|
||||
if (!has_enum(*e)) {
|
||||
enums_.emplace_back(*e);
|
||||
add_element(e->get_namespace(), std::move(e));
|
||||
auto ns = e->get_relative_namespace();
|
||||
add_element(ns, std::move(e));
|
||||
}
|
||||
else
|
||||
LOG_DBG("Enum {} already in the model", e->name());
|
||||
@@ -90,13 +112,14 @@ std::string diagram::to_alias(const std::string &full_name) const
|
||||
LOG_DBG("Looking for alias for {}", full_name);
|
||||
|
||||
for (const auto &c : classes_) {
|
||||
if (c->full_name() == full_name) {
|
||||
const auto &cc = c.get();
|
||||
if (cc.full_name() == full_name) {
|
||||
return c->alias();
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &e : enums_) {
|
||||
if (e->full_name() == full_name) {
|
||||
if (e.get().full_name() == full_name) {
|
||||
return e->alias();
|
||||
}
|
||||
}
|
||||
@@ -104,4 +127,5 @@ std::string diagram::to_alias(const std::string &full_name) const
|
||||
throw error::uml_alias_missing(
|
||||
fmt::format("Missing alias for {}", full_name));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,9 +40,9 @@ public:
|
||||
diagram &operator=(const diagram &) = delete;
|
||||
diagram &operator=(diagram &&) = default;
|
||||
|
||||
const std::vector<type_safe::object_ref<class_>> classes() const;
|
||||
const std::vector<type_safe::object_ref<const class_>> classes() const;
|
||||
|
||||
const std::vector<type_safe::object_ref<enum_>> enums() const;
|
||||
const std::vector<type_safe::object_ref<const enum_>> enums() const;
|
||||
|
||||
bool has_class(const class_ &c) const;
|
||||
|
||||
@@ -58,9 +58,11 @@ public:
|
||||
|
||||
std::string to_alias(const std::string &full_name) const;
|
||||
|
||||
friend void print_diagram_tree(const diagram &d, const int level);
|
||||
|
||||
private:
|
||||
std::vector<type_safe::object_ref<class_>> classes_;
|
||||
std::vector<type_safe::object_ref<enum_>> enums_;
|
||||
std::vector<type_safe::object_ref<const class_, false>> classes_;
|
||||
std::vector<type_safe::object_ref<const enum_, false>> enums_;
|
||||
std::map<std::string, std::unique_ptr<type_alias>> type_aliases_;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -106,9 +106,9 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
|
||||
|
||||
if (!util::starts_with(usn, package_path)) {
|
||||
auto p = std::make_unique<common::model::package>(
|
||||
ctx.config().using_namespace());
|
||||
util::split(
|
||||
ctx.config().using_namespace()[0], "::"));
|
||||
util::remove_prefix(package_path, usn);
|
||||
util::remove_prefix(package_parent, usn);
|
||||
|
||||
p->set_name(e.name());
|
||||
p->set_namespace(package_parent);
|
||||
@@ -129,8 +129,7 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
|
||||
}
|
||||
|
||||
if (!p->skip()) {
|
||||
ctx.diagram().add_element(
|
||||
package_parent, std::move(p));
|
||||
ctx.diagram().add_package(std::move(p));
|
||||
ctx.set_current_package(
|
||||
ctx.diagram()
|
||||
.get_element<common::model::package>(
|
||||
@@ -190,7 +189,9 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
|
||||
}
|
||||
|
||||
if (ctx.config().should_include(
|
||||
cx::util::fully_prefixed(ctx.get_namespace(), cls)))
|
||||
ctx.get_namespace(), cls.name()))
|
||||
// cx::util::fully_prefixed(ctx.get_namespace(),
|
||||
// cls)))
|
||||
process_class_declaration(cls);
|
||||
}
|
||||
else if (e.kind() == cppast::cpp_entity_kind::enum_t) {
|
||||
@@ -201,7 +202,9 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
|
||||
auto &enm = static_cast<const cppast::cpp_enum &>(e);
|
||||
|
||||
if (ctx.config().should_include(
|
||||
cx::util::fully_prefixed(ctx.get_namespace(), enm)))
|
||||
ctx.get_namespace(), enm.name()))
|
||||
// cx::util::fully_prefixed(ctx.get_namespace(),
|
||||
// enm)))
|
||||
process_enum_declaration(enm);
|
||||
}
|
||||
else if (e.kind() == cppast::cpp_entity_kind::type_alias_t) {
|
||||
@@ -210,7 +213,7 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
|
||||
cppast::to_string(e.kind()));
|
||||
|
||||
auto &ta = static_cast<const cppast::cpp_type_alias &>(e);
|
||||
std::unique_ptr<type_alias> t;
|
||||
auto t = std::make_unique<type_alias>();
|
||||
t->set_alias(cx::util::full_name(ctx.get_namespace(), ta));
|
||||
t->set_underlying_type(cx::util::full_name(ta.underlying_type(),
|
||||
ctx.entity_index(), cx::util::is_inside_class(e)));
|
||||
@@ -237,9 +240,15 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
|
||||
else {
|
||||
auto tinst = build_template_instantiation(static_cast<
|
||||
const cppast::cpp_template_instantiation_type &>(
|
||||
at.type_alias().underlying_type()));
|
||||
resolve_alias(at.type_alias().underlying_type())));
|
||||
|
||||
ctx.diagram().add_class(std::move(tinst));
|
||||
ctx.add_type_alias_template(
|
||||
cx::util::full_name(ctx.get_namespace(), at),
|
||||
type_safe::ref(at.type_alias().underlying_type()));
|
||||
|
||||
if (ctx.config().should_include(
|
||||
tinst->get_namespace(), tinst->name()))
|
||||
ctx.diagram().add_class(std::move(tinst));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -254,9 +263,10 @@ void translation_unit_visitor::process_enum_declaration(
|
||||
return;
|
||||
}
|
||||
|
||||
auto e_ptr = std::make_unique<enum_>(ctx.config().using_namespace());
|
||||
auto e_ptr = std::make_unique<enum_>(
|
||||
util::split(ctx.config().using_namespace()[0], "::"));
|
||||
auto &e = *e_ptr;
|
||||
e.set_name(cx::util::full_name(ctx.get_namespace(), enm));
|
||||
e.set_name(enm.name());
|
||||
e.set_namespace(ctx.get_namespace());
|
||||
|
||||
if (enm.comment().has_value())
|
||||
@@ -284,7 +294,9 @@ void translation_unit_visitor::process_enum_declaration(
|
||||
e.add_relationship({relationship_t::kContainment,
|
||||
cx::util::full_name(ctx.get_namespace(), cur.value())});
|
||||
|
||||
LOG_DBG("Added containment relationship {} +-- {}", e.name());
|
||||
LOG_DBG("Added containment relationship {} +-- {}",
|
||||
cx::util::full_name(ctx.get_namespace(), cur.value()),
|
||||
e.name());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -296,10 +308,12 @@ void translation_unit_visitor::process_class_declaration(
|
||||
const cppast::cpp_class &cls,
|
||||
type_safe::optional_ref<const cppast::cpp_template_specialization> tspec)
|
||||
{
|
||||
auto c_ptr = std::make_unique<class_>(ctx.config().using_namespace());
|
||||
auto c_ptr = std::make_unique<class_>(
|
||||
util::split(ctx.config().using_namespace()[0], "::"));
|
||||
auto &c = *c_ptr;
|
||||
c.is_struct(cls.class_kind() == cppast::cpp_class_kind::struct_t);
|
||||
c.set_name(cx::util::full_name(ctx.get_namespace(), cls));
|
||||
// c.set_name(cx::util::full_name(ctx.get_namespace(), cls));
|
||||
c.set_name(cls.name());
|
||||
c.set_namespace(ctx.get_namespace());
|
||||
|
||||
if (cls.comment().has_value())
|
||||
@@ -576,6 +590,7 @@ void translation_unit_visitor::process_class_declaration(
|
||||
static_cast<const char *>(cls.user_data()),
|
||||
fmt::ptr(reinterpret_cast<const void *>(&cls)));
|
||||
|
||||
assert(c_ptr);
|
||||
ctx.diagram().add_class(std::move(c_ptr));
|
||||
}
|
||||
|
||||
@@ -588,6 +603,8 @@ bool translation_unit_visitor::process_field_with_template_instantiation(
|
||||
|
||||
bool res = false;
|
||||
|
||||
auto tr_declaration = cppast::to_string(tr);
|
||||
|
||||
const auto &template_instantiation_type =
|
||||
static_cast<const cppast::cpp_template_instantiation_type &>(tr);
|
||||
|
||||
@@ -595,7 +612,14 @@ bool translation_unit_visitor::process_field_with_template_instantiation(
|
||||
static_cast<const cppast::cpp_template_instantiation_type &>(
|
||||
resolve_alias(template_instantiation_type));
|
||||
|
||||
auto tinst_ptr = build_template_instantiation(unaliased);
|
||||
auto tr_unaliased_declaration = cppast::to_string(unaliased);
|
||||
|
||||
std::unique_ptr<class_> tinst_ptr;
|
||||
if (util::contains(tr_declaration, "<"))
|
||||
tinst_ptr = build_template_instantiation(template_instantiation_type);
|
||||
else
|
||||
tinst_ptr = build_template_instantiation(unaliased);
|
||||
|
||||
auto &tinst = *tinst_ptr;
|
||||
|
||||
// Infer the relationship of this field to the template
|
||||
@@ -604,7 +628,6 @@ bool translation_unit_visitor::process_field_with_template_instantiation(
|
||||
if (mv.type().kind() == cppast::cpp_type_kind::pointer_t ||
|
||||
mv.type().kind() == cppast::cpp_type_kind::reference_t)
|
||||
relationship_type = relationship_t::kAssociation;
|
||||
|
||||
else
|
||||
relationship_type = relationship_t::kAggregation;
|
||||
|
||||
@@ -623,13 +646,19 @@ bool translation_unit_visitor::process_field_with_template_instantiation(
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.config().should_include(tinst.name())) {
|
||||
if (ctx.config().should_include(tinst.get_namespace(), tinst.name())) {
|
||||
LOG_DBG("Adding field instantiation relationship {} {} {} : {}",
|
||||
rr.destination(), clanguml::common::model::to_string(rr.type()),
|
||||
c.full_name(), rr.label());
|
||||
|
||||
c.add_relationship(std::move(rr));
|
||||
|
||||
if (tr_declaration != tr_unaliased_declaration) {
|
||||
// Add template instantiation/specialization relationship;
|
||||
tinst.add_relationship(
|
||||
{relationship_t::kInstantiation, tr_unaliased_declaration});
|
||||
}
|
||||
|
||||
res = true;
|
||||
|
||||
LOG_DBG("Created template instantiation: {}", tinst.full_name());
|
||||
@@ -668,11 +697,14 @@ void translation_unit_visitor::process_field(
|
||||
else if (tr.kind() == cppast::cpp_type_kind::user_defined_t) {
|
||||
LOG_DBG("Processing user defined type field {} {}",
|
||||
cppast::to_string(tr), mv.name());
|
||||
if (resolve_alias(tr).kind() ==
|
||||
cppast::cpp_type_kind::template_instantiation_t)
|
||||
template_instantiation_added_as_aggregation =
|
||||
process_field_with_template_instantiation(mv, tr, c, m, as);
|
||||
}
|
||||
else if (tr.kind() == cppast::cpp_type_kind::template_instantiation_t) {
|
||||
template_instantiation_added_as_aggregation =
|
||||
process_field_with_template_instantiation(
|
||||
mv, resolve_alias(tr), c, m, as);
|
||||
process_field_with_template_instantiation(mv, tr, c, m, as);
|
||||
}
|
||||
else if (tr.kind() == cppast::cpp_type_kind::unexposed_t) {
|
||||
LOG_DBG(
|
||||
@@ -680,6 +712,8 @@ void translation_unit_visitor::process_field(
|
||||
// TODO
|
||||
}
|
||||
|
||||
auto tr_declaration = cppast::to_string(tr);
|
||||
|
||||
if (!m.skip_relationship() &&
|
||||
!template_instantiation_added_as_aggregation &&
|
||||
(tr.kind() != cppast::cpp_type_kind::builtin_t) &&
|
||||
@@ -929,8 +963,10 @@ void translation_unit_visitor::process_function_parameter(
|
||||
find_relationships(cppast::remove_cv(param.type()), relationships,
|
||||
relationship_t::kDependency);
|
||||
for (const auto &[type, relationship_type] : relationships) {
|
||||
if ((relationship_type != relationship_t::kNone) &&
|
||||
(type != c.name())) {
|
||||
|
||||
if (ctx.config().should_include(cx::util::split_ns(type)) &&
|
||||
(relationship_type != relationship_t::kNone) &&
|
||||
(type != c.name_and_ns())) {
|
||||
relationship r{relationship_t::kDependency, type};
|
||||
|
||||
LOG_DBG("Adding field relationship {} {} {} : {}",
|
||||
@@ -954,11 +990,11 @@ void translation_unit_visitor::process_function_parameter(
|
||||
|
||||
// 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(ctx.get_namespace(),
|
||||
template_instantiation_type.primary_template()
|
||||
.get(ctx.entity_index())[0]
|
||||
.get());
|
||||
// auto primary_template_name =
|
||||
// cx::util::full_name(ctx.get_namespace(),
|
||||
// template_instantiation_type.primary_template()
|
||||
// .get(ctx.entity_index())[0]
|
||||
// .get());
|
||||
// Now check if the template arguments of this function param
|
||||
// are a subset of the method template params - if yes this is
|
||||
// not an instantiation but just a reference to an existing
|
||||
@@ -987,10 +1023,15 @@ void translation_unit_visitor::process_function_parameter(
|
||||
// arguments string
|
||||
}
|
||||
|
||||
LOG_DBG("Maybe building instantiation for: {}",
|
||||
primary_template_name);
|
||||
// LOG_DBG("Maybe building instantiation for:
|
||||
// {}",
|
||||
// primary_template_name);
|
||||
|
||||
if (ctx.config().should_include(primary_template_name)) {
|
||||
if (ctx.config().should_include(ctx.get_namespace(),
|
||||
template_instantiation_type.primary_template()
|
||||
.get(ctx.entity_index())[0]
|
||||
.get()
|
||||
.name())) {
|
||||
|
||||
if (template_is_not_instantiation) {
|
||||
LOG_DBG("Template is not an instantiation - "
|
||||
@@ -1073,9 +1114,11 @@ void translation_unit_visitor::process_friend(
|
||||
return;
|
||||
|
||||
if (f.type()) {
|
||||
auto name = cppast::to_string(f.type().value());
|
||||
|
||||
if (!ctx.config().should_include(name))
|
||||
auto name_with_ns =
|
||||
util::split(cppast::to_string(f.type().value()), "::");
|
||||
auto name = name_with_ns.back();
|
||||
name_with_ns.pop_back();
|
||||
if (!ctx.config().should_include(name_with_ns, name))
|
||||
return;
|
||||
|
||||
LOG_DBG("Type friend declaration {}", name);
|
||||
@@ -1113,7 +1156,7 @@ void translation_unit_visitor::process_friend(
|
||||
name = cx::util::full_name(ctx.get_namespace(), f.entity().value());
|
||||
}
|
||||
|
||||
if (!ctx.config().should_include(name))
|
||||
if (!ctx.config().should_include(ctx.get_namespace(), name))
|
||||
return;
|
||||
|
||||
r.set_destination(name);
|
||||
@@ -1192,7 +1235,7 @@ bool translation_unit_visitor::find_relationships(const cppast::cpp_type &t_,
|
||||
else if (t.kind() == cppast::cpp_type_kind::template_instantiation_t) {
|
||||
// class_relationship r;
|
||||
|
||||
auto &tinst =
|
||||
const auto &tinst =
|
||||
static_cast<const cppast::cpp_template_instantiation_type &>(t);
|
||||
|
||||
if (!tinst.arguments_exposed()) {
|
||||
@@ -1201,28 +1244,47 @@ bool translation_unit_visitor::find_relationships(const cppast::cpp_type &t_,
|
||||
return found;
|
||||
}
|
||||
|
||||
const auto &args = tinst.arguments().value();
|
||||
assert(tinst.arguments().has_value());
|
||||
assert(tinst.arguments().value().size() > 0u);
|
||||
|
||||
[[maybe_unused]] const auto args_count =
|
||||
tinst.arguments().value().size();
|
||||
|
||||
const auto args = tinst.arguments().value();
|
||||
|
||||
const auto [ns, base_name] = cx::util::split_ns(fn);
|
||||
|
||||
auto ns_and_name = ns;
|
||||
ns_and_name.push_back(base_name);
|
||||
|
||||
auto full_name = fmt::format("{}", fmt::join(ns_and_name, "::"));
|
||||
|
||||
// Try to match common containers
|
||||
// TODO: Refactor to a separate class with configurable
|
||||
// container list
|
||||
if (name.find("std::unique_ptr") == 0) {
|
||||
if (full_name.find("std::unique_ptr") == 0) {
|
||||
found = find_relationships(args[0u].type().value(), relationships,
|
||||
relationship_t::kAggregation);
|
||||
}
|
||||
else if (name.find("std::shared_ptr") == 0) {
|
||||
else if (full_name.find("std::shared_ptr") == 0) {
|
||||
found = find_relationships(args[0u].type().value(), relationships,
|
||||
relationship_t::kAssociation);
|
||||
}
|
||||
else if (name.find("std::weak_ptr") == 0) {
|
||||
else if (full_name.find("std::weak_ptr") == 0) {
|
||||
found = find_relationships(args[0u].type().value(), relationships,
|
||||
relationship_t::kAssociation);
|
||||
}
|
||||
else if (name.find("std::vector") == 0) {
|
||||
found = find_relationships(args[0u].type().value(), relationships,
|
||||
relationship_t::kAggregation);
|
||||
else if (full_name.find("std::vector") == 0) {
|
||||
assert(args.size() == 1u);
|
||||
if (args[0u].type().has_value())
|
||||
found = find_relationships(args[0u].type().value(),
|
||||
relationships, relationship_t::kAggregation);
|
||||
else
|
||||
LOG_WARN(
|
||||
"Failed to process template argument of std::vector at: {}",
|
||||
fn);
|
||||
}
|
||||
else if (ctx.config().should_include(fn)) {
|
||||
else if (ctx.config().should_include(ns, name)) {
|
||||
LOG_DBG("User defined template instantiation: {} | {}",
|
||||
cppast::to_string(t_), cppast::to_string(t_.canonical()));
|
||||
|
||||
@@ -1245,9 +1307,12 @@ bool translation_unit_visitor::find_relationships(const cppast::cpp_type &t_,
|
||||
|
||||
return found;
|
||||
}
|
||||
// ???
|
||||
else {
|
||||
for (const auto &arg : args) {
|
||||
if (arg.type()) {
|
||||
if (arg.type().has_value()) {
|
||||
LOG_DBG("########## PROCESSING PARAMETER TYPE: {}",
|
||||
cppast::to_string(arg.type().value()));
|
||||
found = find_relationships(
|
||||
arg.type().value(), relationships, relationship_type);
|
||||
}
|
||||
@@ -1262,7 +1327,8 @@ std::unique_ptr<class_> translation_unit_visitor::build_template_instantiation(
|
||||
const cppast::cpp_template_instantiation_type &t,
|
||||
std::optional<clanguml::class_diagram::model::class_ *> parent)
|
||||
{
|
||||
auto tinst_ptr = std::make_unique<class_>(ctx.config().using_namespace());
|
||||
auto tinst_ptr = std::make_unique<class_>(
|
||||
util::split(ctx.config().using_namespace()[0], "::"));
|
||||
auto &tinst = *tinst_ptr;
|
||||
std::string full_template_name;
|
||||
|
||||
@@ -1270,6 +1336,8 @@ std::unique_ptr<class_> translation_unit_visitor::build_template_instantiation(
|
||||
|
||||
tinst.set_namespace(ctx.get_namespace());
|
||||
|
||||
auto tinst_full_name = cppast::to_string(t);
|
||||
|
||||
if (t.primary_template().get(ctx.entity_index()).size()) {
|
||||
const auto &primary_template_ref =
|
||||
static_cast<const cppast::cpp_class_template &>(
|
||||
@@ -1361,28 +1429,37 @@ std::unique_ptr<class_> translation_unit_visitor::build_template_instantiation(
|
||||
LOG_DBG("Building template instantiation for {}", full_template_name);
|
||||
|
||||
// Extract namespace from base template name
|
||||
std::vector<std::string> ns_toks;
|
||||
ns_toks = clanguml::util::split(
|
||||
full_template_name.substr(0, full_template_name.find('<')), "::");
|
||||
// std::vector<std::string> ns_toks;
|
||||
// ns_toks = clanguml::util::split(
|
||||
// full_template_name.substr(0, full_template_name.find('<')), "::");
|
||||
//
|
||||
// std::string ns;
|
||||
// if (ns_toks.size() > 1) {
|
||||
// ns = fmt::format(
|
||||
// "{}::", fmt::join(ns_toks.begin(), ns_toks.end() - 1, "::"));
|
||||
// }
|
||||
|
||||
std::string ns;
|
||||
if (ns_toks.size() > 1) {
|
||||
ns = fmt::format(
|
||||
"{}::", fmt::join(ns_toks.begin(), ns_toks.end() - 1, "::"));
|
||||
// LOG_DBG("Template namespace is {}", ns);
|
||||
|
||||
const auto [ns, name] = cx::util::split_ns(tinst_full_name);
|
||||
tinst.set_name(name);
|
||||
if (ns.empty())
|
||||
tinst.set_namespace(ctx.get_namespace());
|
||||
else
|
||||
tinst.set_namespace(ns);
|
||||
|
||||
if (tinst_full_name.find('<') != std::string::npos) {
|
||||
tinst.set_name(tinst_full_name.substr(0, tinst_full_name.find('<')));
|
||||
}
|
||||
|
||||
LOG_DBG("Template namespace is {}", ns);
|
||||
|
||||
tinst.set_name(ns + util::split(cppast::to_string(t), "<")[0]);
|
||||
|
||||
tinst.is_template_instantiation(true);
|
||||
|
||||
if (tinst.full_name().substr(0, tinst.full_name().find('<')).find("::") ==
|
||||
std::string::npos) {
|
||||
tinst.set_name(ns + tinst.full_name());
|
||||
tinst.set_name(name);
|
||||
}
|
||||
|
||||
// Process template argumetns
|
||||
// Process template arguments
|
||||
int arg_index{0};
|
||||
bool variadic_params{false};
|
||||
if (t.arguments_exposed()) {
|
||||
@@ -1398,6 +1475,8 @@ std::unique_ptr<class_> translation_unit_visitor::build_template_instantiation(
|
||||
cx::util::unreferenced(targ.type().value())),
|
||||
ctx.entity_index(), false);
|
||||
|
||||
auto [fn_ns, fn_name] = cx::util::split_ns(fn);
|
||||
|
||||
if (targ.type().value().kind() ==
|
||||
cppast::cpp_type_kind::template_instantiation_t) {
|
||||
|
||||
@@ -1409,9 +1488,11 @@ std::unique_ptr<class_> translation_unit_visitor::build_template_instantiation(
|
||||
if (parent)
|
||||
nnn = (*parent)->name();
|
||||
|
||||
auto [tinst_ns, tinst_name] =
|
||||
cx::util::split_ns(tinst.full_name(false));
|
||||
auto nested_tinst =
|
||||
build_template_instantiation(nested_template_parameter,
|
||||
ctx.config().should_include(tinst.full_name(false))
|
||||
ctx.config().should_include(tinst_ns, tinst_name)
|
||||
? std::make_optional(&tinst)
|
||||
: parent);
|
||||
|
||||
@@ -1420,11 +1501,13 @@ std::unique_ptr<class_> translation_unit_visitor::build_template_instantiation(
|
||||
|
||||
auto nested_tinst_full_name = nested_tinst->full_name();
|
||||
|
||||
if (ctx.config().should_include(fn)) {
|
||||
if (ctx.config().should_include(fn_ns, fn_name)) {
|
||||
ctx.diagram().add_class(std::move(nested_tinst));
|
||||
}
|
||||
|
||||
if (ctx.config().should_include(tinst.full_name(false))) {
|
||||
if (ctx.config().should_include(tinst_ns, tinst_name) &&
|
||||
ctx.config().should_include(cx::util::split_ns(
|
||||
tinst_dependency.destination()))) {
|
||||
LOG_DBG(
|
||||
"Creating nested template dependency to template "
|
||||
"instantiation {}, {} -> {}",
|
||||
@@ -1463,7 +1546,7 @@ std::unique_ptr<class_> translation_unit_visitor::build_template_instantiation(
|
||||
"type {} -> {}",
|
||||
tinst.full_name(), tinst_dependency.destination());
|
||||
|
||||
if (ctx.config().should_include(fn)) {
|
||||
if (ctx.config().should_include(fn_ns, fn_name)) {
|
||||
tinst.add_relationship(std::move(tinst_dependency));
|
||||
}
|
||||
else if (parent) {
|
||||
@@ -1547,12 +1630,32 @@ const cppast::cpp_type &translation_unit_visitor::resolve_alias(
|
||||
const cppast::cpp_type &type)
|
||||
{
|
||||
const auto &raw_type = cppast::remove_cv(cx::util::unreferenced(type));
|
||||
const auto type_full_name =
|
||||
auto type_full_name =
|
||||
cx::util::full_name(raw_type, ctx.entity_index(), false);
|
||||
if (ctx.has_type_alias(type_full_name)) {
|
||||
|
||||
if (util::contains(type_full_name, "<"))
|
||||
type_full_name = util::split(type_full_name, "<")[0];
|
||||
|
||||
if (ctx.has_type_alias_template(type_full_name)) {
|
||||
return ctx.get_type_alias(type_full_name).get();
|
||||
}
|
||||
else if (ctx.has_type_alias(type_full_name)) {
|
||||
return ctx.get_type_alias_final(raw_type).get();
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
const cppast::cpp_type &translation_unit_visitor::resolve_alias_template(
|
||||
const cppast::cpp_type &type)
|
||||
{
|
||||
const auto &raw_type = cppast::remove_cv(cx::util::unreferenced(type));
|
||||
const auto type_full_name =
|
||||
cx::util::full_name(raw_type, ctx.entity_index(), false);
|
||||
if (ctx.has_type_alias_template(type_full_name)) {
|
||||
return ctx.get_type_alias_template(type_full_name).get();
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +130,8 @@ private:
|
||||
* If t does not represent an alias, returns t.
|
||||
*/
|
||||
const cppast::cpp_type &resolve_alias(const cppast::cpp_type &t);
|
||||
const cppast::cpp_type &resolve_alias_template(
|
||||
const cppast::cpp_type &type);
|
||||
|
||||
// ctx allows to track current visitor context, e.g. current namespace
|
||||
translation_unit_context ctx;
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#include "util/util.h"
|
||||
|
||||
#include <ostream>
|
||||
|
||||
namespace clanguml::common::model {
|
||||
|
||||
std::atomic_uint64_t element::m_nextId = 1;
|
||||
@@ -28,6 +30,8 @@ element::element(const std::vector<std::string> &using_namespaces)
|
||||
: using_namespaces_{using_namespaces}
|
||||
, m_id{m_nextId++}
|
||||
{
|
||||
for (const auto &n : using_namespaces_)
|
||||
assert(!util::contains(n, "::"));
|
||||
}
|
||||
|
||||
std::string element::alias() const { return fmt::format("C_{:010}", m_id); }
|
||||
@@ -41,6 +45,13 @@ void element::add_relationship(relationship &&cr)
|
||||
return;
|
||||
}
|
||||
|
||||
if ((cr.type() == relationship_t::kInstantiation) &&
|
||||
(cr.destination() == full_name(true))) {
|
||||
LOG_WARN("Skipping self instantiation relationship for {}",
|
||||
cr.destination());
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("Adding relationship: '{}' - {} - '{}'", cr.destination(),
|
||||
to_string(cr.type()), full_name(true));
|
||||
|
||||
@@ -50,6 +61,9 @@ void element::add_relationship(relationship &&cr)
|
||||
|
||||
void element::set_using_namespaces(const std::vector<std::string> &un)
|
||||
{
|
||||
for (const auto &n : un)
|
||||
assert(!util::contains(n, "::"));
|
||||
|
||||
using_namespaces_ = un;
|
||||
}
|
||||
|
||||
@@ -71,4 +85,14 @@ bool operator==(const element &l, const element &r)
|
||||
{
|
||||
return l.full_name(false) == r.full_name(false);
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const element &rhs)
|
||||
{
|
||||
out << "(" << rhs.name() << ", ns=["
|
||||
<< util::join(rhs.get_namespace(), "::") << "], full_name=["
|
||||
<< rhs.full_name(true) << "])";
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,8 +19,10 @@
|
||||
|
||||
#include "decorated_element.h"
|
||||
#include "relationship.h"
|
||||
#include "util/util.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -38,10 +40,24 @@ public:
|
||||
|
||||
std::string name() const { return name_; }
|
||||
|
||||
std::string name_and_ns() const
|
||||
{
|
||||
auto ns = namespace_;
|
||||
ns.push_back(name());
|
||||
return util::join(ns, "::");
|
||||
}
|
||||
|
||||
void set_namespace(const std::vector<std::string> &ns) { namespace_ = ns; }
|
||||
|
||||
std::vector<std::string> get_namespace() const { return namespace_; }
|
||||
|
||||
std::vector<std::string> get_relative_namespace() const
|
||||
{
|
||||
auto relative_ns = namespace_;
|
||||
util::remove_prefix(relative_ns, using_namespaces_);
|
||||
return relative_ns;
|
||||
}
|
||||
|
||||
virtual std::string full_name(bool relative) const { return name(); }
|
||||
|
||||
void set_using_namespaces(const std::vector<std::string> &un);
|
||||
@@ -58,6 +74,8 @@ public:
|
||||
|
||||
friend bool operator==(const element &l, const element &r);
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &out, const element &rhs);
|
||||
|
||||
protected:
|
||||
const uint64_t m_id{0};
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <type_safe/optional_ref.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -55,7 +56,7 @@ public:
|
||||
{
|
||||
assert(p);
|
||||
|
||||
LOG_DBG("Adding nested element {} at path '{}'", p->name(),
|
||||
LOG_DBG("Adding nested element {} at path {}", p->name(),
|
||||
fmt::join(path, "::"));
|
||||
|
||||
if (path.empty()) {
|
||||
@@ -68,9 +69,11 @@ public:
|
||||
if (parent && dynamic_cast<nested_trait<T> *>(&parent.value()))
|
||||
dynamic_cast<nested_trait<T> &>(parent.value())
|
||||
.template add_element<V>(std::move(p));
|
||||
else
|
||||
else {
|
||||
spdlog::error(
|
||||
"No parent element found at: {}", fmt::join(path, "::"));
|
||||
throw std::runtime_error("No parent element found");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename V = T>
|
||||
@@ -105,7 +108,7 @@ public:
|
||||
[&](const auto &p) { return name == p->name(); });
|
||||
|
||||
if (it == elements_.end())
|
||||
return type_safe::optional_ref<V>{};
|
||||
return type_safe::optional_ref<V>{type_safe::nullopt};
|
||||
|
||||
assert(it->get() != nullptr);
|
||||
|
||||
@@ -113,7 +116,7 @@ public:
|
||||
return type_safe::optional_ref<V>{
|
||||
type_safe::ref<V>(dynamic_cast<V &>(*it->get()))};
|
||||
|
||||
return type_safe::optional_ref<V>{};
|
||||
return type_safe::optional_ref<V>{type_safe::nullopt};
|
||||
}
|
||||
|
||||
bool has_element(const std::string &name) const
|
||||
@@ -132,6 +135,24 @@ public:
|
||||
auto begin() const { return elements_.begin(); }
|
||||
auto end() const { return elements_.end(); }
|
||||
|
||||
void print_tree(const int level)
|
||||
{
|
||||
const auto &d = *this;
|
||||
|
||||
if (level == 0) {
|
||||
std::cout << "--- Printing tree:\n";
|
||||
}
|
||||
for (const auto &e : d) {
|
||||
if (dynamic_cast<nested_trait<T> *>(e.get())) {
|
||||
std::cout << std::string(level, ' ') << "[" << *e << "]\n";
|
||||
dynamic_cast<nested_trait<T> *>(e.get())->print_tree(level + 1);
|
||||
}
|
||||
else {
|
||||
std::cout << std::string(level, ' ') << "- " << *e << "]\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<T>> elements_;
|
||||
};
|
||||
|
||||
@@ -129,6 +129,20 @@ bool diagram::should_include_relationship(const std::string &rel)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool diagram::should_include(
|
||||
const std::pair<std::vector<std::string>, std::string> &name) const
|
||||
{
|
||||
return should_include(std::get<0>(name), std::get<1>(name));
|
||||
}
|
||||
|
||||
bool diagram::should_include(
|
||||
const std::vector<std::string> &ns, const std::string &name) const
|
||||
{
|
||||
auto ns_and_name = ns;
|
||||
ns_and_name.push_back(name);
|
||||
return should_include(util::join(ns_and_name, "::"));
|
||||
}
|
||||
|
||||
bool diagram::should_include(const std::string &name_) const
|
||||
{
|
||||
auto name = clanguml::util::unqualify(name_);
|
||||
|
||||
@@ -87,8 +87,7 @@ struct inheritable_diagram_options {
|
||||
option<plantuml> puml{"plantuml", option_inherit_mode::append};
|
||||
option<method_arguments> generate_method_arguments{
|
||||
"generate_method_arguments", method_arguments::full};
|
||||
option<bool> generate_packages{
|
||||
"generate_packages", false};
|
||||
option<bool> generate_packages{"generate_packages", false};
|
||||
|
||||
void inherit(const inheritable_diagram_options &parent);
|
||||
};
|
||||
@@ -104,9 +103,16 @@ struct diagram : public inheritable_diagram_options {
|
||||
|
||||
bool should_include_relationship(const std::string &rel);
|
||||
|
||||
bool should_include(const std::string &name_) const;
|
||||
bool should_include(
|
||||
const std::pair<std::vector<std::string>, std::string> &name) const;
|
||||
|
||||
bool should_include(
|
||||
const std::vector<std::string> &ns, const std::string &name) const;
|
||||
|
||||
bool should_include(const common::model::scope_t scope) const;
|
||||
bool should_include(const std::string &name_) const;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
struct source_location {
|
||||
|
||||
@@ -126,6 +126,16 @@ bool is_inside_class(const cppast::cpp_entity &e)
|
||||
return false;
|
||||
}
|
||||
|
||||
std::pair<std::vector<std::string>, std::string> split_ns(
|
||||
const std::string &full_name)
|
||||
{
|
||||
auto name_before_template = ::clanguml::util::split(full_name, "<")[0];
|
||||
auto ns = ::clanguml::util::split(name_before_template, "::");
|
||||
auto name = ns.back();
|
||||
ns.pop_back();
|
||||
return {ns, name};
|
||||
}
|
||||
|
||||
std::string ns(const cppast::cpp_type &t, const cppast::cpp_entity_index &idx)
|
||||
{
|
||||
if (t.kind() == cppast::cpp_type_kind::user_defined_t &&
|
||||
|
||||
@@ -58,6 +58,9 @@ type_safe::optional_ref<const cppast::cpp_namespace> entity_ns(
|
||||
|
||||
std::string ns(const cppast::cpp_type &t, const cppast::cpp_entity_index &idx);
|
||||
|
||||
std::pair<std::vector<std::string>, std::string> split_ns(
|
||||
const std::string &full_name);
|
||||
|
||||
bool is_inside_class(const cppast::cpp_entity &e);
|
||||
} // namespace util
|
||||
} // namespace cx
|
||||
|
||||
@@ -353,6 +353,7 @@ bool translation_unit_visitor::find_relationships(const cppast::cpp_type &t_,
|
||||
const auto fn = cx::util::full_name(
|
||||
resolve_alias(cppast::remove_cv(t_)), ctx.entity_index(), false);
|
||||
auto t_ns = util::split(fn, "::");
|
||||
auto t_name = t_ns.back();
|
||||
t_ns.pop_back();
|
||||
|
||||
const auto &t_raw = resolve_alias(cppast::remove_cv(t_));
|
||||
@@ -465,7 +466,7 @@ bool translation_unit_visitor::find_relationships(const cppast::cpp_type &t_,
|
||||
found = find_relationships(args[0u].type().value(), relationships,
|
||||
relationship_t::kDependency);
|
||||
}
|
||||
else if (ctx.config().should_include(fn)) {
|
||||
else if (ctx.config().should_include(t_ns, t_name)) {
|
||||
LOG_DBG("User defined template instantiation: {} | {}",
|
||||
cppast::to_string(t_), cppast::to_string(t_.canonical()));
|
||||
|
||||
|
||||
@@ -80,7 +80,8 @@ void translation_unit_visitor::process_activities(const cppast::cpp_function &e)
|
||||
.value();
|
||||
m.from = cx::util::ns(caller) + "::" + caller.name();
|
||||
|
||||
if (!ctx.config().should_include(m.from))
|
||||
if (!ctx.config().should_include(
|
||||
util::split(cx::util::ns(caller), "::"), caller.name()))
|
||||
continue;
|
||||
|
||||
if (caller.kind() == cpp_entity_kind::function_t)
|
||||
@@ -96,7 +97,8 @@ void translation_unit_visitor::process_activities(const cppast::cpp_function &e)
|
||||
if (callee.kind() == cpp_entity_kind::function_t)
|
||||
m.to += "()";
|
||||
|
||||
if (!ctx.config().should_include(m.to))
|
||||
if (!ctx.config().should_include(
|
||||
util::split(cx::util::ns(callee), "::"), callee.name()))
|
||||
continue;
|
||||
|
||||
m.to_usr = type_safe::get(function_call.get_callee_method_id());
|
||||
|
||||
@@ -44,22 +44,21 @@ std::vector<std::string> split(std::string str, std::string delimiter)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
|
||||
while (str.size()) {
|
||||
int index = str.find(delimiter);
|
||||
if (index != std::string::npos) {
|
||||
result.push_back(str.substr(0, index));
|
||||
str = str.substr(index + delimiter.size());
|
||||
if (str.size() == 0)
|
||||
result.push_back(str);
|
||||
}
|
||||
else {
|
||||
result.push_back(str);
|
||||
str = "";
|
||||
}
|
||||
}
|
||||
|
||||
if (result.empty())
|
||||
if (!contains(str, delimiter))
|
||||
result.push_back(str);
|
||||
else
|
||||
while (str.size()) {
|
||||
int index = str.find(delimiter);
|
||||
if (index != std::string::npos) {
|
||||
result.push_back(str.substr(0, index));
|
||||
str = str.substr(index + delimiter.size());
|
||||
}
|
||||
else {
|
||||
if (!str.empty())
|
||||
result.push_back(str);
|
||||
str = "";
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <algorithm>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace clanguml {
|
||||
@@ -190,6 +191,9 @@ bool contains(const T &container, const E &element)
|
||||
[&element](const auto &e) { return *e == *element; }) !=
|
||||
container.end();
|
||||
}
|
||||
else if constexpr (std::is_same_v<std::remove_cv_t<T>, std::string>) {
|
||||
return container.find(element) != std::string::npos;
|
||||
}
|
||||
else {
|
||||
return std::find(container.begin(), container.end(), element) !=
|
||||
container.end();
|
||||
|
||||
@@ -30,8 +30,8 @@ TEST_CASE("t00002", "[test-case][class]")
|
||||
|
||||
REQUIRE(diagram->exclude().namespaces.size() == 0);
|
||||
|
||||
REQUIRE(diagram->should_include("clanguml::t00002::A"));
|
||||
REQUIRE(!diagram->should_include("std::vector"));
|
||||
REQUIRE(diagram->should_include({"clanguml", "t00002"}, "A"));
|
||||
REQUIRE(!diagram->should_include({"std"}, "vector"));
|
||||
|
||||
auto model = generate_class_diagram(db, diagram);
|
||||
|
||||
|
||||
@@ -39,12 +39,23 @@ TEST_CASE("t00014", "[test-case][class]")
|
||||
REQUIRE_THAT(puml, IsClassTemplate("A", "T,std::string"));
|
||||
REQUIRE_THAT(puml, IsClassTemplate("A", "bool,std::string"));
|
||||
REQUIRE_THAT(puml, IsClassTemplate("AString", "float"));
|
||||
REQUIRE_THAT(puml, IsClassTemplate("AString", "int"));
|
||||
REQUIRE_THAT(puml, IsClassTemplate("AString", "std::string"));
|
||||
REQUIRE_THAT(
|
||||
puml, !IsClassTemplate("std::std::function", "void(T...,int),int)"));
|
||||
|
||||
REQUIRE_THAT(puml, IsInstantiation(_A("A<T,P>"), _A("A<T,std::string>")));
|
||||
REQUIRE_THAT(
|
||||
puml, IsInstantiation(_A("A<T,std::string>"), _A("AString<float>")));
|
||||
REQUIRE_THAT(
|
||||
puml, IsInstantiation(_A("A<T,std::string>"), _A("AString<int>")));
|
||||
REQUIRE_THAT(
|
||||
puml, !IsInstantiation(_A("AString<int>"), _A("AString<int>")));
|
||||
REQUIRE_THAT(puml,
|
||||
IsInstantiation(_A("A<T,std::string>"), _A("AString<std::string>")));
|
||||
REQUIRE_THAT(puml,
|
||||
!IsInstantiation(
|
||||
_A("AString<std::string>"), _A("AString<std::string>")));
|
||||
REQUIRE_THAT(
|
||||
puml, IsAggregation(_A("R"), _A("A<bool,std::string>"), "-boolstring"));
|
||||
REQUIRE_THAT(
|
||||
|
||||
@@ -32,6 +32,8 @@ TEST_CASE("Test split", "[unit-test]")
|
||||
CHECK(split("ABCD", " ") == C{"ABCD"});
|
||||
|
||||
CHECK(split("std::vector::detail", "::") == C{"std", "vector", "detail"});
|
||||
|
||||
CHECK(split("std::vector::detail::", "::") == C{"std", "vector", "detail"});
|
||||
}
|
||||
|
||||
TEST_CASE("Test ns_relative", "[unit-test]")
|
||||
|
||||
Reference in New Issue
Block a user