Added package diagram generation from C++20 modules (#101)

This commit is contained in:
Bartek Kryza
2023-12-21 23:39:59 +01:00
parent f09edd8b47
commit a8d646d1bc
18 changed files with 285 additions and 12 deletions

View File

@@ -25,8 +25,9 @@
namespace clanguml::common::model {
element::element(namespace_ using_namespace)
: using_namespace_{std::move(using_namespace)}
element::element(namespace_ using_namespace, path_type pt)
: ns_{pt}
, using_namespace_{std::move(using_namespace)}
{
}

View File

@@ -37,7 +37,9 @@ namespace clanguml::common::model {
*/
class element : public diagram_element {
public:
element(namespace_ using_namespace);
element(namespace_ using_namespace, path_type pt = path_type::kNamespace);
element(path_type pt);
~element() override = default;

View File

@@ -21,8 +21,8 @@
#include <sstream>
namespace clanguml::common::model {
package::package(const common::model::namespace_ &using_namespace)
: element{using_namespace}
package::package(const common::model::namespace_ &using_namespace, path_type pt)
: element{using_namespace, pt}
{
}

View File

@@ -41,7 +41,8 @@ class package : public element,
public stylable_element,
public nested_trait<element, path> {
public:
package(const common::model::path &using_namespace);
package(const common::model::path &using_namespace,
path_type pt = path_type::kNamespace);
package(const package &) = delete;
package(package &&) = default;

View File

@@ -386,6 +386,8 @@ public:
*/
path_type type() const { return path_type_; }
const container_type &tokens() const { return path_; }
private:
path_type path_type_;
container_type path_;

View File

@@ -135,6 +135,9 @@ public:
if (parent_path.type() == common::model::path_type::kNamespace) {
return add_with_namespace_path(std::move(e));
}
else if (parent_path.type() == common::model::path_type::kModule) {
return add_with_module_path(parent_path, std::move(e));
}
return add_with_filesystem_path(parent_path, std::move(e));
}
@@ -155,6 +158,17 @@ public:
inja::json context() const override;
private:
/**
* @brief Add element using module as diagram path
*
* @tparam ElementT Element type
* @param e Element to add
* @return True, if the element was added
*/
template <typename ElementT>
bool add_with_module_path(
const common::model::path &parent_path, std::unique_ptr<ElementT> &&e);
/**
* @brief Add element using namespace as diagram path
*
@@ -237,6 +251,55 @@ bool diagram::add_with_namespace_path(std::unique_ptr<ElementT> &&p)
return res;
}
template <typename ElementT>
bool diagram::add_with_module_path(
const common::model::path &parent_path, std::unique_ptr<ElementT> &&p)
{
LOG_DBG("Adding package: {}, {}, {}, [{}]", p->name(), p->full_name(false),
parent_path.to_string(), p->id());
// Make sure all parent modules are already packages in the
// model
std::string module_path = p->using_namespace().to_string();
for (auto it = parent_path.begin(); it != parent_path.end(); it++) {
auto pkg = std::make_unique<common::model::package>(
p->using_namespace(), common::model::path_type::kModule);
pkg->set_name(*it);
auto ns = common::model::path(
parent_path.begin(), it, common::model::path_type::kModule);
pkg->set_module(module_path);
pkg->set_namespace(ns);
std::string package_id_path;
if (module_path.empty())
package_id_path = pkg->name();
else
package_id_path = module_path + "." + pkg->name();
pkg->set_id(common::to_id(package_id_path));
auto p_ref = std::ref(*pkg);
auto res = add_element(ns, std::move(pkg));
if (res)
element_view<ElementT>::add(p_ref);
if (module_path.empty())
module_path = *it;
else
module_path += fmt::format(".{}", *it);
}
auto p_ref = std::ref(*p);
auto res = add_element(parent_path, std::move(p));
if (res)
element_view<ElementT>::add(p_ref);
return res;
}
template <typename ElementT>
bool diagram::add_with_filesystem_path(
const common::model::path &parent_path, std::unique_ptr<ElementT> &&p)

View File

@@ -21,6 +21,8 @@
#include "common/clang_utils.h"
#include "common/model/namespace.h"
#include "clang/Basic/Module.h"
#include <spdlog/spdlog.h>
#include <deque>
@@ -45,7 +47,7 @@ bool translation_unit_visitor::VisitNamespaceDecl(clang::NamespaceDecl *ns)
{
assert(ns != nullptr);
if (config().package_type() == config::package_type_t::kDirectory)
if (config().package_type() != config::package_type_t::kNamespace)
return true;
if (ns->isAnonymousNamespace() || ns->isInline())
@@ -237,6 +239,43 @@ void translation_unit_visitor::add_relationships(
if (diagram().should_include(*pkg))
diagram().add(parent_path, std::move(pkg));
}
else if (config().package_type() == config::package_type_t::kModule) {
const auto *module = cls->getOwningModule();
if (module == nullptr) {
return;
}
std::string module_path_str = module->Name;
if (module->isPrivateModule())
module_path_str = module->getTopLevelModule()->Name;
common::model::path module_path{
module_path_str, common::model::path_type::kModule};
module_path.pop_back();
auto relative_module =
config().make_module_relative(std::optional{module_path_str});
common::model::path parent_path{
relative_module, common::model::path_type::kModule};
auto pkg_name = parent_path.name();
parent_path.pop_back();
auto pkg = std::make_unique<common::model::package>(
config().using_module(), common::model::path_type::kModule);
pkg->set_name(pkg_name);
pkg->set_id(get_package_id(cls));
// This is for diagram filters
pkg->set_module(module_path.to_string());
// This is for rendering nested package structure
pkg->set_namespace(parent_path);
set_source_location(*cls, *pkg);
if (diagram().should_include(*pkg))
diagram().add(parent_path, std::move(pkg));
}
auto current_package_id = get_package_id(cls);
@@ -284,6 +323,18 @@ common::model::diagram_element::id_t translation_unit_visitor::get_package_id(
return {};
}
else if (config().package_type() == config::package_type_t::kModule) {
const auto *module = cls->getOwningModule();
if (module != nullptr) {
std::string module_path = module->Name;
if (module->isPrivateModule()) {
module_path = module->getTopLevelModule()->Name;
}
return common::to_id(module_path);
}
return {};
}
auto file =
source_manager().getFilename(cls->getSourceRange().getBegin()).str();
@@ -578,6 +629,15 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type,
}
}
}
else if (config().package_type() ==
config::package_type_t::kModule) {
const auto *module = cxxrecord_decl->getOwningModule();
if (module != nullptr) {
const auto target_id = get_package_id(cxxrecord_decl);
relationships.emplace_back(target_id, relationship_hint);
result = true;
}
}
else {
if (diagram().should_include(
namespace_{common::get_qualified_name(
@@ -591,8 +651,8 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type,
}
else if (const auto *record_decl = type->getAsRecordDecl();
record_decl != nullptr) {
// This is only possible for plain C translation unit, so we don't
// need to consider namespaces here
// This is only possible for plain C translation unit, so we
// don't need to consider namespaces or modules here
if (config().package_type() == config::package_type_t::kDirectory) {
if (diagram().should_include(
namespace_{common::get_qualified_name(*record_decl)})) {