Skip to content

Commit

Permalink
Added package diagram generation from C++20 modules (#101)
Browse files Browse the repository at this point in the history
  • Loading branch information
bkryza committed Dec 21, 2023
1 parent f09edd8 commit a8d646d
Show file tree
Hide file tree
Showing 18 changed files with 285 additions and 12 deletions.
5 changes: 3 additions & 2 deletions src/common/model/element.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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)}
{
}

Expand Down
4 changes: 3 additions & 1 deletion src/common/model/element.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
4 changes: 2 additions & 2 deletions src/common/model/package.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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}
{
}

Expand Down
3 changes: 2 additions & 1 deletion src/common/model/package.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/common/model/path.h
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,8 @@ class path {
*/
path_type type() const { return path_type_; }

const container_type &tokens() const { return path_; }

private:
path_type path_type_;
container_type path_;
Expand Down
63 changes: 63 additions & 0 deletions src/package_diagram/model/diagram.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ class diagram : public clanguml::common::model::diagram,
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));
}
Expand All @@ -155,6 +158,17 @@ class diagram : public clanguml::common::model::diagram,
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
*
Expand Down Expand Up @@ -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)
Expand Down
66 changes: 63 additions & 3 deletions src/package_diagram/visitor/translation_unit_visitor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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())
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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(
Expand All @@ -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)})) {
Expand Down
2 changes: 1 addition & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ file(GLOB_RECURSE TEST_CONFIG_YMLS test_config_data/*.yml
test_compilation_database_data/*.json)

set(TEST_CASES_REQUIRING_CXX20 t00056 t00058 t00059 t00065 t00069)
set(TEST_CASES_REQUIRING_CXX20_MODULES t00070 t00071)
set(TEST_CASES_REQUIRING_CXX20_MODULES t00070 t00071 t30012)

if(ENABLE_CXX_MODULES_TEST_CASES)
foreach(CXX20_MOD_TC ${TEST_CASES_REQUIRING_CXX20_MODULES})
Expand Down
10 changes: 10 additions & 0 deletions tests/t30012/.clang-uml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
diagrams:
t30012_package:
type: package
glob:
- t30012.cc
package_type: module
include:
modules:
- t30012
using_module: t30012
13 changes: 13 additions & 0 deletions tests/t30012/src/lib1.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export module t30012.app.lib1;

export namespace clanguml::t30012 {
class B { };

template <typename T> class BB {
T t;
};

namespace detail {
enum class BBB { bbb1, bbb2 };
} // namespace detail
}
5 changes: 5 additions & 0 deletions tests/t30012/src/lib1mod1.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export module t30012.app.lib1.mod1;

export namespace clanguml::t30012 {
class D { };
}
5 changes: 5 additions & 0 deletions tests/t30012/src/lib1mod2.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export module t30012.app.lib1.mod2;

export namespace clanguml::t30012 {
class E { };
}
13 changes: 13 additions & 0 deletions tests/t30012/src/lib2.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export module t30012.app.lib2;

export namespace clanguml::t30012 {
class C { };

template <typename T> class CC {
T t;
};

namespace detail {
enum class CCC { ccc1, ccc2 };
}
}
11 changes: 11 additions & 0 deletions tests/t30012/src/t30012_mod.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export module t30012.app;
export import t30012.app.lib1;
export import t30012.app.lib2;

export namespace clanguml::t30012 {
class A {
int get() { return a; }

int a;
};
}
15 changes: 15 additions & 0 deletions tests/t30012/t30012.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import t30012.app;
import t30012.app.lib1;
import t30012.app.lib1.mod1;
import t30012.app.lib1.mod2;
import t30012.app.lib2;

namespace clanguml {
namespace t30012 {
class R {
A *a;
B *b;
C *c;
};
}
}
Loading

0 comments on commit a8d646d

Please sign in to comment.