Merge pull request #16 from bkryza/enable-packages-in-class-diagrams
Enable packages in class diagrams
11
README.md
@@ -2,11 +2,11 @@
|
||||
|
||||

|
||||
|
||||
`clang-uml` is an automatic C++ to [PlantUML](https://plantuml.com) class and sequence
|
||||
diagram generator, driven by YAML configuration files. The main idea behind the
|
||||
`clang-uml` is an automatic C++ to [PlantUML](https://plantuml.com) class, sequence
|
||||
and package diagram generator, driven by YAML configuration files. The main idea behind the
|
||||
project is to easily maintain up-to-date diagrams within a code-base or document
|
||||
existing project code. The configuration file or files for `clang-uml` define the
|
||||
type and contents of each diagram.
|
||||
type and contents of each generated diagram.
|
||||
|
||||
## Features
|
||||
Main features supported so far include:
|
||||
@@ -17,11 +17,14 @@ Main features supported so far include:
|
||||
* Template instantiation relationships
|
||||
* Relationship inference from C++ containers and smart pointers
|
||||
* Namespace based content filtering
|
||||
* Optional package generation from namespaces
|
||||
* Sequence diagram generation
|
||||
* Generation of sequence diagram from one code location to another
|
||||
* Generation of sequence diagram from one code location to another (currently only for non-template code)
|
||||
* Package diagram generation
|
||||
* Generation of package diagram based on C++ namespaces
|
||||
|
||||
To see what `clang-uml` can do so far, checkout the diagrams generated for unit test cases [here](./docs/test_cases.md).
|
||||
|
||||
## Installation
|
||||
|
||||
### Building from source
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
* [t00033](./test_cases/t00033.md) - Nested template instantiation dependency test case
|
||||
* [t00034](./test_cases/t00034.md) - Template metaprogramming type function test case
|
||||
* [t00035](./test_cases/t00035.md) - PlantUML class diagram layout hints test case
|
||||
* [t00036](./test_cases/t00036.md) - Class diagram with namespaces generated as packages
|
||||
## Sequence diagrams
|
||||
* [t20001](./test_cases/t20001.md) - Basic sequence diagram test case
|
||||
* [t20002](./test_cases/t20002.md) - Free function sequence diagram test case
|
||||
|
||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
57
docs/test_cases/t00036.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# t00036 - Class diagram with namespaces generated as packages
|
||||
## Config
|
||||
```yaml
|
||||
compilation_database_dir: ..
|
||||
output_directory: puml
|
||||
diagrams:
|
||||
t00036_class:
|
||||
type: class
|
||||
generate_packages: true
|
||||
glob:
|
||||
- ../../tests/t00036/t00036.cc
|
||||
using_namespace:
|
||||
- clanguml::t00036
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::t00036
|
||||
```
|
||||
## Source code
|
||||
File t00036.cc
|
||||
```cpp
|
||||
namespace clanguml {
|
||||
namespace t00036 {
|
||||
|
||||
namespace ns1 {
|
||||
|
||||
enum class E { blue, yellow };
|
||||
|
||||
namespace ns11 {
|
||||
|
||||
template <typename T> struct A {
|
||||
T a;
|
||||
};
|
||||
|
||||
namespace ns111 {
|
||||
|
||||
struct B {
|
||||
A<int> a_int;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace ns2 {
|
||||
namespace ns22 {
|
||||
|
||||
struct C;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace t00036
|
||||
} // namespace clanguml
|
||||
|
||||
```
|
||||
## Generated UML diagrams
|
||||

|
||||
BIN
docs/test_cases/t00036_class.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.9 KiB |
@@ -33,20 +33,30 @@ void generator::generate_alias(const class_ &c, std::ostream &ostr) const
|
||||
if (c.is_abstract())
|
||||
class_type = "abstract";
|
||||
|
||||
ostr << class_type << " \"" << c.full_name();
|
||||
auto full_name = c.full_name();
|
||||
|
||||
if (m_config.generate_packages())
|
||||
ostr << class_type << " \"" << c.full_name_no_ns();
|
||||
else
|
||||
ostr << class_type << " \"" << c.full_name();
|
||||
|
||||
ostr << "\" as " << c.alias() << '\n';
|
||||
}
|
||||
|
||||
void generator::generate_alias(const enum_ &e, std::ostream &ostr) const
|
||||
{
|
||||
ostr << "enum"
|
||||
<< " \"" << e.full_name();
|
||||
if (m_config.generate_packages())
|
||||
ostr << "enum"
|
||||
<< " \"" << e.name();
|
||||
else
|
||||
ostr << "enum"
|
||||
<< " \"" << e.full_name();
|
||||
|
||||
ostr << "\" as " << e.alias() << '\n';
|
||||
}
|
||||
|
||||
void generator::generate(const class_ &c, std::ostream &ostr) const
|
||||
void generator::generate(
|
||||
const class_ &c, std::ostream &ostr, std::ostream &relationships_ostr) const
|
||||
{
|
||||
namespace plantuml_common = clanguml::common::generators::plantuml;
|
||||
|
||||
@@ -89,7 +99,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);
|
||||
}
|
||||
@@ -213,10 +223,11 @@ void generator::generate(const class_ &c, std::ostream &ostr) const
|
||||
generate_notes(ostr, c);
|
||||
|
||||
// Print relationships
|
||||
ostr << all_relations_str.str();
|
||||
relationships_ostr << all_relations_str.str();
|
||||
}
|
||||
|
||||
void generator::generate(const enum_ &e, std::ostream &ostr) const
|
||||
void generator::generate(
|
||||
const enum_ &e, std::ostream &ostr, std::ostream &relationships_ostr) const
|
||||
{
|
||||
ostr << "enum " << e.alias();
|
||||
|
||||
@@ -255,7 +266,7 @@ void generator::generate(const enum_ &e, std::ostream &ostr) const
|
||||
|
||||
relstr << '\n';
|
||||
|
||||
ostr << relstr.str();
|
||||
relationships_ostr << relstr.str();
|
||||
}
|
||||
catch (error::uml_alias_missing &ex) {
|
||||
LOG_ERROR("Skipping {} relation from {} to {} due "
|
||||
@@ -269,40 +280,72 @@ void generator::generate(const enum_ &e, std::ostream &ostr) const
|
||||
generate_notes(ostr, e);
|
||||
}
|
||||
|
||||
void generator::generate(const package &p, std::ostream &ostr,
|
||||
std::ostream &relationships_ostr) const
|
||||
{
|
||||
if (m_config.generate_packages()) {
|
||||
LOG_DBG("Generating package {}", p.name());
|
||||
|
||||
ostr << "package [" << p.name() << "] ";
|
||||
ostr << "as " << p.alias();
|
||||
|
||||
if (p.is_deprecated())
|
||||
ostr << " <<deprecated>>";
|
||||
|
||||
if (!p.style().empty())
|
||||
ostr << " " << p.style();
|
||||
|
||||
ostr << " {" << '\n';
|
||||
}
|
||||
|
||||
for (const auto &subpackage : p) {
|
||||
if (dynamic_cast<package *>(subpackage.get())) {
|
||||
generate(
|
||||
dynamic_cast<package &>(*subpackage), ostr, relationships_ostr);
|
||||
}
|
||||
if (dynamic_cast<class_ *>(subpackage.get())) {
|
||||
generate_alias(dynamic_cast<class_ &>(*subpackage), ostr);
|
||||
generate(
|
||||
dynamic_cast<class_ &>(*subpackage), ostr, relationships_ostr);
|
||||
}
|
||||
if (dynamic_cast<enum_ *>(subpackage.get())) {
|
||||
generate_alias(dynamic_cast<enum_ &>(*subpackage), ostr);
|
||||
generate(
|
||||
dynamic_cast<enum_ &>(*subpackage), ostr, relationships_ostr);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_config.generate_packages()) {
|
||||
ostr << "}" << '\n';
|
||||
}
|
||||
|
||||
generate_notes(ostr, p);
|
||||
}
|
||||
|
||||
void generator::generate(std::ostream &ostr) const
|
||||
{
|
||||
ostr << "@startuml" << '\n';
|
||||
|
||||
std::stringstream relationship_ostr;
|
||||
|
||||
generate_plantuml_directives(ostr, m_config.puml().before);
|
||||
|
||||
if (m_config.should_include_entities("classes")) {
|
||||
for (const auto &c : m_model.classes()) {
|
||||
if (!m_config.should_include(c.name()))
|
||||
continue;
|
||||
generate_alias(c, ostr);
|
||||
ostr << '\n';
|
||||
for (const auto &p : m_model) {
|
||||
if (dynamic_cast<package *>(p.get())) {
|
||||
generate(dynamic_cast<package &>(*p), ostr, relationship_ostr);
|
||||
}
|
||||
|
||||
for (const auto &e : m_model.enums()) {
|
||||
if (!m_config.should_include(e.name()))
|
||||
continue;
|
||||
generate_alias(e, ostr);
|
||||
ostr << '\n';
|
||||
if (dynamic_cast<class_ *>(p.get())) {
|
||||
generate_alias(dynamic_cast<class_ &>(*p), ostr);
|
||||
generate(dynamic_cast<class_ &>(*p), ostr, relationship_ostr);
|
||||
}
|
||||
|
||||
for (const auto &c : m_model.classes()) {
|
||||
if (!m_config.should_include(c.name()))
|
||||
continue;
|
||||
generate(c, ostr);
|
||||
ostr << '\n';
|
||||
if (dynamic_cast<enum_ *>(p.get())) {
|
||||
generate_alias(dynamic_cast<enum_ &>(*p), ostr);
|
||||
generate(dynamic_cast<enum_ &>(*p), ostr, relationship_ostr);
|
||||
}
|
||||
ostr << '\n';
|
||||
}
|
||||
|
||||
if (m_config.should_include_entities("enums"))
|
||||
for (const auto &e : m_model.enums()) {
|
||||
generate(e, ostr);
|
||||
ostr << '\n';
|
||||
}
|
||||
ostr << relationship_ostr.str();
|
||||
|
||||
generate_config_layout_hints(ostr);
|
||||
|
||||
@@ -310,5 +353,4 @@ void generator::generate(std::ostream &ostr) const
|
||||
|
||||
ostr << "@enduml" << '\n';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ using common_generator =
|
||||
|
||||
using clanguml::class_diagram::model::class_;
|
||||
using clanguml::class_diagram::model::enum_;
|
||||
using clanguml::common::model::package;
|
||||
using clanguml::common::model::relationship_t;
|
||||
using clanguml::common::model::scope_t;
|
||||
|
||||
@@ -62,9 +63,14 @@ public:
|
||||
|
||||
void generate_alias(const enum_ &e, std::ostream &ostr) const;
|
||||
|
||||
void generate(const class_ &c, std::ostream &ostr) const;
|
||||
void generate(const class_ &c, std::ostream &ostr,
|
||||
std::ostream &relationships_ostr) const;
|
||||
|
||||
void generate(const enum_ &e, std::ostream &ostr) const;
|
||||
void generate(const enum_ &e, std::ostream &ostr,
|
||||
std::ostream &relationships_ostr) const;
|
||||
|
||||
void generate(const package &p, std::ostream &ostr,
|
||||
std::ostream &relationships_ostr) const;
|
||||
|
||||
void generate(std::ostream &ostr) const override;
|
||||
};
|
||||
|
||||
@@ -96,16 +96,36 @@ void class_::add_type_alias(type_alias &&ta)
|
||||
type_aliases_[ta.alias()] = std::move(ta);
|
||||
}
|
||||
|
||||
std::string class_::full_name_no_ns() const
|
||||
{
|
||||
using namespace clanguml::util;
|
||||
|
||||
std::ostringstream ostr;
|
||||
|
||||
ostr << name();
|
||||
|
||||
render_template_params(ostr);
|
||||
|
||||
return ostr.str();
|
||||
}
|
||||
|
||||
std::string class_::full_name(bool relative) const
|
||||
{
|
||||
using namespace clanguml::util;
|
||||
|
||||
std::ostringstream ostr;
|
||||
if (relative)
|
||||
ostr << ns_relative(using_namespaces(), name());
|
||||
if (relative && starts_with(get_namespace(), using_namespaces()))
|
||||
ostr << ns_relative(using_namespaces(), name_and_ns());
|
||||
else
|
||||
ostr << name();
|
||||
ostr << name_and_ns();
|
||||
|
||||
render_template_params(ostr);
|
||||
|
||||
return ostr.str();
|
||||
}
|
||||
std::ostringstream &class_::render_template_params(
|
||||
std::ostringstream &ostr) const
|
||||
{
|
||||
if (!templates_.empty()) {
|
||||
std::vector<std::string> tnames;
|
||||
std::transform(templates_.cbegin(), templates_.cend(),
|
||||
@@ -114,11 +134,11 @@ std::string class_::full_name(bool relative) const
|
||||
|
||||
if (!tmplt.type().empty())
|
||||
res.push_back(
|
||||
ns_relative(using_namespaces(), tmplt.type()));
|
||||
util::ns_relative(using_namespaces(), tmplt.type()));
|
||||
|
||||
if (!tmplt.name().empty())
|
||||
res.push_back(
|
||||
ns_relative(using_namespaces(), tmplt.name()));
|
||||
util::ns_relative(using_namespaces(), tmplt.name()));
|
||||
|
||||
if (!tmplt.default_value().empty()) {
|
||||
res.push_back("=");
|
||||
@@ -129,8 +149,7 @@ std::string class_::full_name(bool relative) const
|
||||
});
|
||||
ostr << fmt::format("<{}>", fmt::join(tnames, ","));
|
||||
}
|
||||
|
||||
return ostr.str();
|
||||
return ostr;
|
||||
}
|
||||
|
||||
bool class_::is_abstract() const
|
||||
|
||||
@@ -36,6 +36,11 @@ class class_ : public common::model::element,
|
||||
public:
|
||||
class_(const std::vector<std::string> &using_namespaces);
|
||||
|
||||
class_(const class_ &) = delete;
|
||||
class_(class_ &&) noexcept = delete;
|
||||
class_ &operator=(const class_ &) = delete;
|
||||
class_ &operator=(class_ &&) = delete;
|
||||
|
||||
bool is_struct() const;
|
||||
void is_struct(bool is_struct);
|
||||
|
||||
@@ -64,9 +69,13 @@ public:
|
||||
|
||||
std::string full_name(bool relative = true) const override;
|
||||
|
||||
std::string full_name_no_ns() const;
|
||||
|
||||
bool is_abstract() const;
|
||||
|
||||
private:
|
||||
std::ostringstream &render_template_params(std::ostringstream &ostr) const;
|
||||
|
||||
bool is_struct_{false};
|
||||
bool is_template_{false};
|
||||
bool is_template_instantiation_{false};
|
||||
|
||||
@@ -21,42 +21,89 @@
|
||||
#include "util/error.h"
|
||||
#include "util/util.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
namespace clanguml::class_diagram::model {
|
||||
|
||||
const std::vector<class_> diagram::classes() const { return classes_; }
|
||||
const std::vector<type_safe::object_ref<const class_>> diagram::classes() const
|
||||
{
|
||||
return classes_;
|
||||
}
|
||||
|
||||
const std::vector<enum_> diagram::enums() const { return enums_; }
|
||||
const std::vector<type_safe::object_ref<const enum_>> diagram::enums() const
|
||||
{
|
||||
return enums_;
|
||||
}
|
||||
|
||||
bool diagram::has_class(const class_ &c) const
|
||||
{
|
||||
return std::any_of(classes_.cbegin(), classes_.cend(),
|
||||
[&c](const auto &cc) { return cc.full_name() == c.full_name(); });
|
||||
[&c](const auto &cc) { return cc.get().full_name() == c.full_name(); });
|
||||
}
|
||||
|
||||
void diagram::add_type_alias(type_alias &&ta)
|
||||
bool diagram::has_enum(const enum_ &e) const
|
||||
{
|
||||
LOG_DBG("Adding global alias: {} -> {}", ta.alias(), ta.underlying_type());
|
||||
|
||||
type_aliases_[ta.alias()] = std::move(ta);
|
||||
return std::any_of(enums_.cbegin(), enums_.cend(),
|
||||
[&e](const auto &ee) { return ee.get().full_name() == e.full_name(); });
|
||||
}
|
||||
|
||||
void diagram::add_class(class_ &&c)
|
||||
void diagram::add_type_alias(std::unique_ptr<type_alias> &&ta)
|
||||
{
|
||||
LOG_DBG("Adding class: {}, {}", c.name(), c.full_name());
|
||||
if (!has_class(c))
|
||||
classes_.emplace_back(std::move(c));
|
||||
LOG_DBG(
|
||||
"Adding global alias: {} -> {}", ta->alias(), ta->underlying_type());
|
||||
|
||||
type_aliases_[ta->alias()] = std::move(ta);
|
||||
}
|
||||
|
||||
void diagram::add_package(std::unique_ptr<common::model::package> &&p)
|
||||
{
|
||||
LOG_DBG("Adding namespace package: {}, {}", p->name(), p->full_name(true));
|
||||
|
||||
auto ns = p->get_relative_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)) {
|
||||
classes_.emplace_back(*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("Class {} ({}) already in the model", c.name(), c.full_name());
|
||||
LOG_DBG(
|
||||
"Class {} ({}) already in the model", c->name(), c->full_name());
|
||||
}
|
||||
|
||||
void diagram::add_enum(enum_ &&e)
|
||||
void diagram::add_enum(std::unique_ptr<enum_> &&e)
|
||||
{
|
||||
LOG_DBG("Adding enum: {}", e.name());
|
||||
auto it = std::find(enums_.begin(), enums_.end(), e);
|
||||
if (it == enums_.end())
|
||||
enums_.emplace_back(std::move(e));
|
||||
LOG_DBG("Adding enum: {}", e->name());
|
||||
|
||||
assert(!util::contains(e->name(), "::"));
|
||||
|
||||
if (!has_enum(*e)) {
|
||||
enums_.emplace_back(*e);
|
||||
auto ns = e->get_relative_namespace();
|
||||
add_element(ns, std::move(e));
|
||||
}
|
||||
else
|
||||
LOG_DBG("Enum {} already in the model", e.name());
|
||||
LOG_DBG("Enum {} already in the model", e->name());
|
||||
}
|
||||
|
||||
std::string diagram::to_alias(const std::string &full_name) const
|
||||
@@ -64,18 +111,20 @@ 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) {
|
||||
return c.alias();
|
||||
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) {
|
||||
return e.alias();
|
||||
if (e.get().full_name() == full_name) {
|
||||
return e->alias();
|
||||
}
|
||||
}
|
||||
|
||||
throw error::uml_alias_missing(
|
||||
fmt::format("Missing alias for {}", full_name));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
#include "class.h"
|
||||
#include "common/model/diagram.h"
|
||||
#include "common/model/nested_trait.h"
|
||||
#include "common/model/package.h"
|
||||
#include "enum.h"
|
||||
#include "type_alias.h"
|
||||
|
||||
@@ -27,7 +29,9 @@
|
||||
|
||||
namespace clanguml::class_diagram::model {
|
||||
|
||||
class diagram : public clanguml::common::model::diagram {
|
||||
class diagram : public clanguml::common::model::diagram,
|
||||
public clanguml::common::model::nested_trait<
|
||||
clanguml::common::model::element> {
|
||||
public:
|
||||
diagram() = default;
|
||||
|
||||
@@ -36,23 +40,29 @@ public:
|
||||
diagram &operator=(const diagram &) = delete;
|
||||
diagram &operator=(diagram &&) = default;
|
||||
|
||||
const std::vector<class_> classes() const;
|
||||
const std::vector<type_safe::object_ref<const class_>> classes() const;
|
||||
|
||||
const std::vector<enum_> enums() const;
|
||||
const std::vector<type_safe::object_ref<const enum_>> enums() const;
|
||||
|
||||
bool has_class(const class_ &c) const;
|
||||
|
||||
void add_type_alias(type_alias &&ta);
|
||||
bool has_enum(const enum_ &e) const;
|
||||
|
||||
void add_class(class_ &&c);
|
||||
void add_type_alias(std::unique_ptr<type_alias> &&ta);
|
||||
|
||||
void add_enum(enum_ &&e);
|
||||
void add_class(std::unique_ptr<class_> &&c);
|
||||
|
||||
void add_enum(std::unique_ptr<enum_> &&e);
|
||||
|
||||
void add_package(std::unique_ptr<common::model::package> &&p);
|
||||
|
||||
std::string to_alias(const std::string &full_name) const;
|
||||
|
||||
friend void print_diagram_tree(const diagram &d, const int level);
|
||||
|
||||
private:
|
||||
std::vector<class_> classes_;
|
||||
std::vector<enum_> enums_;
|
||||
std::map<std::string, type_alias> type_aliases_;
|
||||
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_;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,7 +29,10 @@ enum_::enum_(const std::vector<std::string> &using_namespaces)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const enum_ &l, const enum_ &r) { return l.name() == r.name(); }
|
||||
bool operator==(const enum_ &l, const enum_ &r)
|
||||
{
|
||||
return (l.get_namespace() == r.get_namespace()) && (l.name() == r.name());
|
||||
}
|
||||
|
||||
std::string enum_::full_name(bool relative) const
|
||||
{
|
||||
|
||||
@@ -29,6 +29,12 @@ class enum_ : public common::model::element,
|
||||
public:
|
||||
enum_(const std::vector<std::string> &using_namespaces);
|
||||
|
||||
enum_(const enum_ &) = delete;
|
||||
enum_(enum_ &&) = default;
|
||||
enum_ &operator=(const enum_ &) = delete;
|
||||
enum_ &operator=(enum_ &&) = default;
|
||||
|
||||
// TODO: Do we need this?
|
||||
friend bool operator==(const enum_ &l, const enum_ &r);
|
||||
|
||||
std::string full_name(bool relative = true) const override;
|
||||
|
||||
@@ -32,6 +32,53 @@ translation_unit_context::translation_unit_context(
|
||||
{
|
||||
}
|
||||
|
||||
bool translation_unit_context::has_namespace_alias(
|
||||
const std::string &full_name) const
|
||||
{
|
||||
bool res =
|
||||
namespace_alias_index_.find(full_name) != namespace_alias_index_.end();
|
||||
|
||||
LOG_DBG("Alias {} {} found in index", full_name, res ? "" : "not");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void translation_unit_context::add_namespace_alias(const std::string &full_name,
|
||||
type_safe::object_ref<const cppast::cpp_namespace> ref)
|
||||
{
|
||||
if (!has_namespace_alias(full_name)) {
|
||||
LOG_DBG(
|
||||
"Stored namespace alias: {} -> {} ", full_name, ref.get().name());
|
||||
|
||||
namespace_alias_index_.emplace(full_name, std::move(ref));
|
||||
}
|
||||
}
|
||||
|
||||
type_safe::object_ref<const cppast::cpp_namespace>
|
||||
translation_unit_context::get_namespace_alias(
|
||||
const std::string &full_name) const
|
||||
{
|
||||
assert(has_namespace_alias(full_name));
|
||||
|
||||
return namespace_alias_index_.at(full_name);
|
||||
}
|
||||
|
||||
type_safe::object_ref<const cppast::cpp_namespace>
|
||||
translation_unit_context::get_namespace_alias_final(
|
||||
const cppast::cpp_namespace &ns) const
|
||||
{
|
||||
auto ns_full_name = cx::util::full_name({}, ns);
|
||||
|
||||
ns_full_name = cx::util::ns(ns) + "::" + ns_full_name;
|
||||
|
||||
if (has_namespace_alias(ns_full_name)) {
|
||||
return get_namespace_alias_final(
|
||||
namespace_alias_index_.at(ns_full_name).get());
|
||||
}
|
||||
|
||||
return type_safe::ref(ns);
|
||||
}
|
||||
|
||||
bool translation_unit_context::has_type_alias(
|
||||
const std::string &full_name) const
|
||||
{
|
||||
@@ -132,4 +179,16 @@ clanguml::class_diagram::model::diagram &translation_unit_context::diagram()
|
||||
return diagram_;
|
||||
}
|
||||
|
||||
void translation_unit_context::set_current_package(
|
||||
type_safe::optional_ref<common::model::package> p)
|
||||
{
|
||||
current_package_ = p;
|
||||
}
|
||||
|
||||
type_safe::optional_ref<common::model::package>
|
||||
translation_unit_context::get_current_package() const
|
||||
{
|
||||
return current_package_;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "config/config.h"
|
||||
|
||||
#include <cppast/cpp_entity_index.hpp>
|
||||
#include <cppast/cpp_namespace.hpp>
|
||||
#include <cppast/cpp_type.hpp>
|
||||
#include <type_safe/reference.hpp>
|
||||
|
||||
@@ -31,6 +32,17 @@ public:
|
||||
clanguml::class_diagram::model::diagram &diagram,
|
||||
const clanguml::config::class_diagram &config);
|
||||
|
||||
bool has_namespace_alias(const std::string &full_name) const;
|
||||
|
||||
void add_namespace_alias(const std::string &full_name,
|
||||
type_safe::object_ref<const cppast::cpp_namespace> ref);
|
||||
|
||||
type_safe::object_ref<const cppast::cpp_namespace> get_namespace_alias(
|
||||
const std::string &full_name) const;
|
||||
|
||||
type_safe::object_ref<const cppast::cpp_namespace>
|
||||
get_namespace_alias_final(const cppast::cpp_namespace &t) const;
|
||||
|
||||
bool has_type_alias(const std::string &full_name) const;
|
||||
|
||||
void add_type_alias(const std::string &full_name,
|
||||
@@ -62,6 +74,10 @@ public:
|
||||
|
||||
clanguml::class_diagram::model::diagram &diagram();
|
||||
|
||||
void set_current_package(type_safe::optional_ref<common::model::package> p);
|
||||
|
||||
type_safe::optional_ref<common::model::package> get_current_package() const;
|
||||
|
||||
private:
|
||||
// Current visitor namespace
|
||||
std::vector<std::string> namespace_;
|
||||
@@ -75,6 +91,10 @@ private:
|
||||
// Reference to class diagram config
|
||||
const clanguml::config::class_diagram &config_;
|
||||
|
||||
// Map of discovered aliases (declared with 'namespace' keyword)
|
||||
std::map<std::string, type_safe::object_ref<const cppast::cpp_namespace>>
|
||||
namespace_alias_index_;
|
||||
|
||||
// Map of discovered aliases (declared with 'using' keyword)
|
||||
std::map<std::string, type_safe::object_ref<const cppast::cpp_type>>
|
||||
alias_index_;
|
||||
@@ -82,6 +102,8 @@ private:
|
||||
// Map of discovered template aliases (declared with 'using' keyword)
|
||||
std::map<std::string, type_safe::object_ref<const cppast::cpp_type>>
|
||||
alias_template_index_;
|
||||
|
||||
type_safe::optional_ref<common::model::package> current_package_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -34,6 +34,11 @@
|
||||
#include <cppast/visitor.hpp>
|
||||
#include <type_safe/reference.hpp>
|
||||
|
||||
#include <class_diagram/model/class.h>
|
||||
#include <common/model/enums.h>
|
||||
#include <cppast/cpp_alias_template.hpp>
|
||||
#include <cppast/cpp_type_alias.hpp>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@@ -41,6 +46,9 @@
|
||||
|
||||
namespace clanguml::class_diagram::visitor {
|
||||
|
||||
using found_relationships_t =
|
||||
std::vector<std::pair<std::string, common::model::relationship_t>>;
|
||||
|
||||
class translation_unit_visitor {
|
||||
public:
|
||||
translation_unit_visitor(cppast::cpp_entity_index &idx,
|
||||
@@ -99,8 +107,7 @@ public:
|
||||
const std::set<std::string> &template_parameter_names = {});
|
||||
|
||||
bool find_relationships(const cppast::cpp_type &t,
|
||||
std::vector<std::pair<std::string,
|
||||
clanguml::common::model::relationship_t>> &relationships,
|
||||
found_relationships_t &relationships,
|
||||
clanguml::common::model::relationship_t relationship_hint =
|
||||
clanguml::common::model::relationship_t::kNone);
|
||||
|
||||
@@ -119,8 +126,42 @@ public:
|
||||
void process_friend(const cppast::cpp_friend &t,
|
||||
clanguml::class_diagram::model::class_ &parent);
|
||||
|
||||
void process_namespace(const cppast::cpp_entity &e,
|
||||
const cppast::cpp_namespace &ns_declaration);
|
||||
|
||||
void process_type_alias(const cppast::cpp_type_alias &ta);
|
||||
|
||||
void process_type_alias_template(const cppast::cpp_alias_template &at);
|
||||
|
||||
void process_class_children(const cppast::cpp_class &cls, model::class_ &c);
|
||||
|
||||
void process_class_bases(
|
||||
const cppast::cpp_class &cls, model::class_ &c) const;
|
||||
|
||||
void process_unexposed_template_specialization_parameters(
|
||||
const type_safe::optional_ref<const cppast::cpp_template_specialization>
|
||||
&tspec,
|
||||
model::class_ &c) const;
|
||||
|
||||
void process_exposed_template_specialization_parameters(
|
||||
const type_safe::optional_ref<const cppast::cpp_template_specialization>
|
||||
&tspec,
|
||||
model::class_ &c);
|
||||
|
||||
void process_scope_template_parameters(
|
||||
model::class_ &c, const cppast::cpp_scope_name &scope);
|
||||
|
||||
bool process_template_parameters(const cppast::cpp_class &cls,
|
||||
model::class_ &c,
|
||||
const type_safe::optional_ref<const cppast::cpp_template_specialization>
|
||||
&tspec);
|
||||
|
||||
void process_class_containment(
|
||||
const cppast::cpp_class &cls, model::class_ &c) const;
|
||||
|
||||
private:
|
||||
clanguml::class_diagram::model::class_ build_template_instantiation(
|
||||
std::unique_ptr<clanguml::class_diagram::model::class_>
|
||||
build_template_instantiation(
|
||||
const cppast::cpp_template_instantiation_type &t,
|
||||
std::optional<clanguml::class_diagram::model::class_ *> parent = {});
|
||||
|
||||
@@ -130,6 +171,54 @@ private:
|
||||
*/
|
||||
const cppast::cpp_type &resolve_alias(const cppast::cpp_type &t);
|
||||
|
||||
const cppast::cpp_type &resolve_alias_template(
|
||||
const cppast::cpp_type &type);
|
||||
|
||||
bool find_relationships_in_array(
|
||||
found_relationships_t &relationships, const cppast::cpp_type &t);
|
||||
|
||||
bool find_relationships_in_pointer(const cppast::cpp_type &t_,
|
||||
found_relationships_t &relationships,
|
||||
const common::model::relationship_t &relationship_hint);
|
||||
|
||||
bool find_relationships_in_reference(const cppast::cpp_type &t_,
|
||||
found_relationships_t &relationships,
|
||||
const common::model::relationship_t &relationship_hint);
|
||||
|
||||
bool find_relationships_in_user_defined_type(const cppast::cpp_type &t_,
|
||||
found_relationships_t &relationships, const std::string &fn,
|
||||
common::model::relationship_t &relationship_type,
|
||||
const cppast::cpp_type &t);
|
||||
|
||||
bool find_relationships_in_template_instantiation(const cppast::cpp_type &t,
|
||||
const std::string &fn, found_relationships_t &relationships,
|
||||
common::model::relationship_t relationship_type);
|
||||
|
||||
void build_template_instantiation_primary_template(
|
||||
const cppast::cpp_template_instantiation_type &t,
|
||||
clanguml::class_diagram::model::class_ &tinst,
|
||||
std::deque<std::tuple<std::string, int, bool>> &template_base_params,
|
||||
std::optional<clanguml::class_diagram::model::class_ *> &parent,
|
||||
std::string &full_template_name) const;
|
||||
|
||||
void build_template_instantiation_process_type_argument(
|
||||
const std::optional<clanguml::class_diagram::model::class_ *> &parent,
|
||||
model::class_ &tinst, const cppast::cpp_template_argument &targ,
|
||||
model::class_template &ct);
|
||||
|
||||
void build_template_instantiation_process_expression_argument(
|
||||
const cppast::cpp_template_argument &targ,
|
||||
model::class_template &ct) const;
|
||||
|
||||
bool build_template_instantiation_add_base_classes(model::class_ &tinst,
|
||||
std::deque<std::tuple<std::string, int, bool>> &template_base_params,
|
||||
int arg_index, bool variadic_params,
|
||||
const model::class_template &ct) const;
|
||||
|
||||
void process_function_parameter_find_relationships_in_template(
|
||||
model::class_ &c, const std::set<std::string> &template_parameter_names,
|
||||
const cppast::cpp_type &t);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -66,4 +80,19 @@ const std::vector<relationship> &element::relationships() const
|
||||
}
|
||||
|
||||
void element::append(const element &e) { decorated_element::append(e); }
|
||||
|
||||
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>
|
||||
|
||||
@@ -30,16 +32,32 @@ class element : public decorated_element {
|
||||
public:
|
||||
element(const std::vector<std::string> &using_namespaces);
|
||||
|
||||
virtual ~element() = default;
|
||||
|
||||
std::string alias() const;
|
||||
|
||||
void set_name(const std::string &name) { name_ = name; }
|
||||
|
||||
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);
|
||||
@@ -54,6 +72,10 @@ public:
|
||||
|
||||
void append(const element &e);
|
||||
|
||||
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>
|
||||
|
||||
@@ -37,7 +38,7 @@ public:
|
||||
|
||||
virtual ~nested_trait() = default;
|
||||
|
||||
void add_element(std::unique_ptr<T> p)
|
||||
template <typename V = T> void add_element(std::unique_ptr<V> p)
|
||||
{
|
||||
auto it = std::find_if(elements_.begin(), elements_.end(),
|
||||
[&p](const auto &e) { return *e == *p; });
|
||||
@@ -50,11 +51,12 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void add_element(std::vector<std::string> path, std::unique_ptr<T> p)
|
||||
template <typename V = T>
|
||||
void add_element(std::vector<std::string> path, std::unique_ptr<V> p)
|
||||
{
|
||||
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()) {
|
||||
@@ -64,42 +66,57 @@ public:
|
||||
|
||||
auto parent = get_element(path);
|
||||
|
||||
if (parent)
|
||||
parent.value().add_element(std::move(p));
|
||||
else
|
||||
if (parent && dynamic_cast<nested_trait<T> *>(&parent.value()))
|
||||
dynamic_cast<nested_trait<T> &>(parent.value())
|
||||
.template add_element<V>(std::move(p));
|
||||
else {
|
||||
spdlog::error(
|
||||
"No parent element found at: {}", fmt::join(path, "::"));
|
||||
throw std::runtime_error("No parent element found");
|
||||
}
|
||||
}
|
||||
|
||||
type_safe::optional_ref<T> get_element(std::vector<std::string> path) const
|
||||
template <typename V = T>
|
||||
auto get_element(std::vector<std::string> path) const
|
||||
{
|
||||
LOG_DBG("Getting nested element at path: {}", fmt::join(path, "::"));
|
||||
|
||||
if (path.empty() || !has_element(path.at(0))) {
|
||||
LOG_WARN("Nested element {} not found in element",
|
||||
fmt::join(path, "::"));
|
||||
return {};
|
||||
return type_safe::optional_ref<V>{};
|
||||
}
|
||||
|
||||
auto p = get_element(path.at(0));
|
||||
if (path.size() == 1)
|
||||
return p;
|
||||
return get_element<V>(path.at(0));
|
||||
|
||||
return p.value().get_element(
|
||||
std::vector<std::string>(path.begin() + 1, path.end()));
|
||||
auto p = get_element<T>(path.at(0));
|
||||
|
||||
if (!p)
|
||||
return type_safe::optional_ref<V>{};
|
||||
|
||||
if (dynamic_cast<nested_trait<T> *>(&p.value()))
|
||||
return dynamic_cast<nested_trait<T> &>(p.value()).get_element<V>(
|
||||
std::vector<std::string>(path.begin() + 1, path.end()));
|
||||
|
||||
return type_safe::optional_ref<V>{};
|
||||
}
|
||||
|
||||
type_safe::optional_ref<T> get_element(const std::string &name) const
|
||||
template <typename V = T> auto get_element(const std::string &name) const
|
||||
{
|
||||
auto it = std::find_if(elements_.cbegin(), elements_.cend(),
|
||||
[&](const auto &p) { return name == p->name(); });
|
||||
|
||||
if (it == elements_.end())
|
||||
return {};
|
||||
return type_safe::optional_ref<V>{type_safe::nullopt};
|
||||
|
||||
assert(it->get() != nullptr);
|
||||
|
||||
return type_safe::ref(*(it->get()));
|
||||
if (dynamic_cast<V *>(it->get()))
|
||||
return type_safe::optional_ref<V>{
|
||||
type_safe::ref<V>(dynamic_cast<V &>(*it->get()))};
|
||||
|
||||
return type_safe::optional_ref<V>{type_safe::nullopt};
|
||||
}
|
||||
|
||||
bool has_element(const std::string &name) const
|
||||
@@ -118,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_;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* src/package_diagram/model/class.h
|
||||
* src/common/model/class.h
|
||||
*
|
||||
* Copyright (c) 2021-2022 Bartek Kryza <bkryza@gmail.com>
|
||||
*
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace clanguml::package_diagram::model {
|
||||
namespace clanguml::common::model {
|
||||
package::package(const std::vector<std::string> &using_namespaces)
|
||||
: element{using_namespaces}
|
||||
{
|
||||
@@ -43,11 +43,6 @@ std::string package::full_name(bool relative) const
|
||||
return fmt::format("{}", fmt::join(fn, "::"));
|
||||
}
|
||||
|
||||
bool operator==(const package &l, const package &r)
|
||||
{
|
||||
return l.full_name(false) == r.full_name(false);
|
||||
}
|
||||
|
||||
bool package::is_deprecated() const { return is_deprecated_; }
|
||||
|
||||
void package::set_deprecated(bool deprecated) { is_deprecated_ = deprecated; }
|
||||
@@ -29,11 +29,11 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace clanguml::package_diagram::model {
|
||||
namespace clanguml::common::model {
|
||||
|
||||
class package : public common::model::element,
|
||||
public common::model::stylable_element,
|
||||
public common::model::nested_trait<package> {
|
||||
class package : public element,
|
||||
public stylable_element,
|
||||
public nested_trait<element> {
|
||||
public:
|
||||
package(const std::vector<std::string> &using_namespaces);
|
||||
|
||||
@@ -44,8 +44,6 @@ public:
|
||||
|
||||
std::string full_name(bool relative) const override;
|
||||
|
||||
friend bool operator==(const package &l, const package &r);
|
||||
|
||||
bool is_deprecated() const;
|
||||
|
||||
void set_deprecated(bool deprecated);
|
||||
@@ -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_);
|
||||
@@ -150,7 +164,7 @@ bool diagram::should_include(const std::string &name_) const
|
||||
return true;
|
||||
}
|
||||
|
||||
spdlog::debug("Skipping from diagram: {}", name);
|
||||
LOG_DBG("Skipping from diagram: {}", name);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -396,6 +410,7 @@ template <> struct convert<class_diagram> {
|
||||
get_option(node, rhs.layout);
|
||||
get_option(node, rhs.include_relations_also_as_members);
|
||||
get_option(node, rhs.generate_method_arguments);
|
||||
get_option(node, rhs.generate_packages);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -476,6 +491,7 @@ template <> struct convert<config> {
|
||||
get_option(node, rhs.include_relations_also_as_members);
|
||||
get_option(node, rhs.puml);
|
||||
get_option(node, rhs.generate_method_arguments);
|
||||
get_option(node, rhs.generate_packages);
|
||||
|
||||
auto diagrams = node["diagrams"];
|
||||
|
||||
|
||||
@@ -87,6 +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};
|
||||
|
||||
void inherit(const inheritable_diagram_options &parent);
|
||||
};
|
||||
@@ -102,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 {
|
||||
|
||||
@@ -56,6 +56,8 @@ template <typename T> struct option {
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(const T &v) { set(v); }
|
||||
|
||||
T &operator()() { return value; }
|
||||
|
||||
const T &operator()() const { return value; }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -54,8 +54,9 @@ void generator::generate_relationships(
|
||||
}
|
||||
|
||||
// Process it's subpackages relationships
|
||||
for (const std::unique_ptr<package> &subpackage : p) {
|
||||
generate_relationships(*subpackage, ostr);
|
||||
for (const auto &subpackage : p) {
|
||||
generate_relationships(
|
||||
dynamic_cast<const package &>(*subpackage), ostr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +78,7 @@ void generator::generate(const package &p, std::ostream &ostr) const
|
||||
ostr << " {" << '\n';
|
||||
|
||||
for (const auto &subpackage : p) {
|
||||
generate(*subpackage, ostr);
|
||||
generate(dynamic_cast<const package &>(*subpackage), ostr);
|
||||
}
|
||||
|
||||
ostr << "}" << '\n';
|
||||
@@ -93,14 +94,14 @@ void generator::generate(std::ostream &ostr) const
|
||||
|
||||
if (m_config.should_include_entities("packages")) {
|
||||
for (const auto &p : m_model) {
|
||||
generate(*p, ostr);
|
||||
generate(dynamic_cast<package &>(*p), ostr);
|
||||
ostr << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
// Process package relationships
|
||||
for (const auto &p : m_model) {
|
||||
generate_relationships(*p, ostr);
|
||||
generate_relationships(dynamic_cast<package &>(*p), ostr);
|
||||
ostr << '\n';
|
||||
}
|
||||
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/generators/plantuml/generator.h"
|
||||
#include "common/model/package.h"
|
||||
#include "common/model/relationship.h"
|
||||
#include "config/config.h"
|
||||
#include "cx/compilation_database.h"
|
||||
#include "package_diagram/model/diagram.h"
|
||||
#include "package_diagram/model/package.h"
|
||||
#include "package_diagram/visitor/translation_unit_visitor.h"
|
||||
#include "util/util.h"
|
||||
|
||||
@@ -46,9 +46,9 @@ template <typename C, typename D>
|
||||
using common_generator =
|
||||
clanguml::common::generators::plantuml::generator<C, D>;
|
||||
|
||||
using clanguml::common::model::package;
|
||||
using clanguml::common::model::relationship_t;
|
||||
using clanguml::common::model::scope_t;
|
||||
using clanguml::package_diagram::model::package;
|
||||
using namespace clanguml::util;
|
||||
|
||||
class generator : public common_generator<diagram_config, diagram_model> {
|
||||
|
||||
@@ -33,7 +33,7 @@ std::string diagram::to_alias(const std::string &full_name) const
|
||||
throw error::uml_alias_missing(
|
||||
fmt::format("Missing alias for '{}'", full_name));
|
||||
|
||||
auto package = get_element(fn);
|
||||
auto package = get_element<common::model::package>(fn);
|
||||
|
||||
if (!package)
|
||||
throw error::uml_alias_missing(
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/model/diagram.h"
|
||||
#include "package.h"
|
||||
#include "common/model/package.h"
|
||||
|
||||
#include <type_safe/optional_ref.hpp>
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
namespace clanguml::package_diagram::model {
|
||||
|
||||
class diagram : public clanguml::common::model::diagram,
|
||||
public clanguml::common::model::nested_trait<package> {
|
||||
public clanguml::common::model::nested_trait<
|
||||
clanguml::common::model::element> {
|
||||
public:
|
||||
diagram() = default;
|
||||
|
||||
|
||||
@@ -180,12 +180,12 @@ clanguml::package_diagram::model::diagram &translation_unit_context::diagram()
|
||||
}
|
||||
|
||||
void translation_unit_context::set_current_package(
|
||||
type_safe::optional_ref<model::package> p)
|
||||
type_safe::optional_ref<common::model::package> p)
|
||||
{
|
||||
current_package_ = p;
|
||||
}
|
||||
|
||||
type_safe::optional_ref<model::package>
|
||||
type_safe::optional_ref<common::model::package>
|
||||
translation_unit_context::get_current_package() const
|
||||
{
|
||||
return current_package_;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "common/model/package.h"
|
||||
#include "config/config.h"
|
||||
#include "package_diagram/model/diagram.h"
|
||||
|
||||
@@ -75,9 +76,9 @@ public:
|
||||
|
||||
clanguml::package_diagram::model::diagram &diagram();
|
||||
|
||||
void set_current_package(type_safe::optional_ref<model::package> p);
|
||||
void set_current_package(type_safe::optional_ref<common::model::package> p);
|
||||
|
||||
type_safe::optional_ref<model::package> get_current_package() const;
|
||||
type_safe::optional_ref<common::model::package> get_current_package() const;
|
||||
|
||||
private:
|
||||
// Current visitor namespace
|
||||
@@ -104,7 +105,7 @@ private:
|
||||
std::map<std::string, type_safe::object_ref<const cppast::cpp_type>>
|
||||
alias_template_index_;
|
||||
|
||||
type_safe::optional_ref<model::package> current_package_;
|
||||
type_safe::optional_ref<common::model::package> current_package_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -37,11 +37,11 @@ namespace clanguml::package_diagram::visitor {
|
||||
|
||||
using clanguml::class_diagram::model::type_alias;
|
||||
using clanguml::common::model::access_t;
|
||||
using clanguml::common::model::package;
|
||||
using clanguml::common::model::relationship;
|
||||
using clanguml::common::model::relationship_t;
|
||||
using clanguml::common::model::scope_t;
|
||||
using clanguml::package_diagram::model::diagram;
|
||||
using clanguml::package_diagram::model::package;
|
||||
|
||||
namespace detail {
|
||||
scope_t cpp_access_specifier_to_scope(
|
||||
@@ -98,10 +98,8 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
|
||||
ctx.config().using_namespace()[0], "::");
|
||||
|
||||
if (!util::starts_with(usn, package_path)) {
|
||||
auto p = std::make_unique<package>(
|
||||
ctx.config().using_namespace());
|
||||
auto p = std::make_unique<package>(usn);
|
||||
util::remove_prefix(package_path, usn);
|
||||
util::remove_prefix(package_parent, usn);
|
||||
|
||||
p->set_name(e.name());
|
||||
p->set_namespace(package_parent);
|
||||
@@ -122,10 +120,11 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
|
||||
}
|
||||
|
||||
if (!p->skip()) {
|
||||
ctx.diagram().add_element(
|
||||
package_parent, std::move(p));
|
||||
auto rns = p->get_relative_namespace();
|
||||
ctx.diagram().add_element(rns, std::move(p));
|
||||
ctx.set_current_package(
|
||||
ctx.diagram().get_element(package_path));
|
||||
ctx.diagram().get_element<package>(
|
||||
package_path));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,6 +351,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_));
|
||||
@@ -464,7 +464,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()));
|
||||
|
||||
|
||||
@@ -35,10 +35,10 @@
|
||||
#include <type_safe/reference.hpp>
|
||||
|
||||
#include <common/model/enums.h>
|
||||
#include <common/model/package.h>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <package_diagram/model/package.h>
|
||||
#include <string>
|
||||
|
||||
namespace clanguml::package_diagram::visitor {
|
||||
|
||||
@@ -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(
|
||||
|
||||
13
tests/t00036/.clang-uml
Normal file
@@ -0,0 +1,13 @@
|
||||
compilation_database_dir: ..
|
||||
output_directory: puml
|
||||
diagrams:
|
||||
t00036_class:
|
||||
type: class
|
||||
generate_packages: true
|
||||
glob:
|
||||
- ../../tests/t00036/t00036.cc
|
||||
using_namespace:
|
||||
- clanguml::t00036
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::t00036
|
||||
33
tests/t00036/t00036.cc
Normal file
@@ -0,0 +1,33 @@
|
||||
namespace clanguml {
|
||||
namespace t00036 {
|
||||
|
||||
namespace ns1 {
|
||||
|
||||
enum class E { blue, yellow };
|
||||
|
||||
namespace ns11 {
|
||||
|
||||
template <typename T> struct A {
|
||||
T a;
|
||||
};
|
||||
|
||||
namespace ns111 {
|
||||
|
||||
struct B {
|
||||
A<int> a_int;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace ns2 {
|
||||
namespace ns22 {
|
||||
|
||||
struct C;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace t00036
|
||||
} // namespace clanguml
|
||||
50
tests/t00036/test_case.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* tests/t00036/test_case.cc
|
||||
*
|
||||
* Copyright (c) 2021-2022 Bartek Kryza <bkryza@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
TEST_CASE("t00036", "[test-case][class]")
|
||||
{
|
||||
auto [config, db] = load_config("t00036");
|
||||
|
||||
auto diagram = config.diagrams["t00036_class"];
|
||||
|
||||
REQUIRE(diagram->name == "t00036_class");
|
||||
REQUIRE(diagram->generate_packages() == true);
|
||||
|
||||
auto model = generate_class_diagram(db, diagram);
|
||||
|
||||
REQUIRE(model.name() == "t00036_class");
|
||||
|
||||
auto puml = generate_class_puml(diagram, model);
|
||||
AliasMatcher _A(puml);
|
||||
|
||||
REQUIRE_THAT(puml, StartsWith("@startuml"));
|
||||
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
|
||||
|
||||
REQUIRE_THAT(puml, IsClassTemplate("A", "T"));
|
||||
REQUIRE_THAT(puml, IsClassTemplate("A", "int"));
|
||||
REQUIRE_THAT(puml, IsEnum(_A("E")));
|
||||
REQUIRE_THAT(puml, IsClass(_A("B")));
|
||||
REQUIRE_THAT(puml, IsClass(_A("C")));
|
||||
REQUIRE_THAT(puml, IsPackage("ns111"));
|
||||
REQUIRE_THAT(puml, IsPackage("ns22"));
|
||||
|
||||
REQUIRE_THAT(puml, IsAggregation(_A("B"), _A("A<int>"), "+a_int"));
|
||||
|
||||
save_puml(
|
||||
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);
|
||||
}
|
||||
@@ -178,6 +178,7 @@ using namespace clanguml::test::matchers;
|
||||
#include "t00033/test_case.h"
|
||||
#include "t00034/test_case.h"
|
||||
#include "t00035/test_case.h"
|
||||
#include "t00036/test_case.h"
|
||||
|
||||
//
|
||||
// Sequence diagram tests
|
||||
|
||||
@@ -102,6 +102,9 @@ test_cases:
|
||||
- name: t00035
|
||||
title: PlantUML class diagram layout hints test case
|
||||
description:
|
||||
- name: t00036
|
||||
title: Class diagram with namespaces generated as packages
|
||||
description:
|
||||
Sequence diagrams:
|
||||
- name: t20001
|
||||
title: Basic sequence diagram test case
|
||||
|
||||
@@ -33,6 +33,7 @@ TEST_CASE("Test config simple", "[unit-test]")
|
||||
CHECK(clanguml::util::contains(diagram.using_namespace(), "clanguml"));
|
||||
CHECK(diagram.generate_method_arguments() ==
|
||||
clanguml::config::method_arguments::full);
|
||||
CHECK(diagram.generate_packages() == true);
|
||||
}
|
||||
|
||||
TEST_CASE("Test config inherited", "[unit-test]")
|
||||
@@ -46,6 +47,7 @@ TEST_CASE("Test config inherited", "[unit-test]")
|
||||
CHECK(def.glob()[0] == "src/**/*.cc");
|
||||
CHECK(def.glob()[1] == "src/**/*.h");
|
||||
CHECK(clanguml::util::contains(def.using_namespace(), "clanguml"));
|
||||
CHECK(def.generate_packages() == false);
|
||||
|
||||
auto &cus = *cfg.diagrams["class_custom"];
|
||||
CHECK(cus.type() == clanguml::config::diagram_type::class_diagram);
|
||||
@@ -53,6 +55,7 @@ TEST_CASE("Test config inherited", "[unit-test]")
|
||||
CHECK(cus.glob()[0] == "src/main.cc");
|
||||
CHECK(clanguml::util::contains(cus.using_namespace(), "clanguml::ns1"));
|
||||
CHECK(cus.include_relations_also_as_members());
|
||||
CHECK(def.generate_packages() == false);
|
||||
}
|
||||
|
||||
TEST_CASE("Test config includes", "[unit-test]")
|
||||
|
||||
@@ -9,6 +9,7 @@ diagrams:
|
||||
using_namespace:
|
||||
- clanguml
|
||||
generate_method_arguments: full
|
||||
generate_packages: true
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml
|
||||
|
||||
@@ -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]")
|
||||
|
||||