Added support for class diagram filtering based on C++20 modules (#195)
This commit is contained in:
@@ -159,6 +159,7 @@ bool translation_unit_visitor::VisitEnumDecl(clang::EnumDecl *enm)
|
||||
|
||||
process_comment(*enm, e);
|
||||
set_source_location(*enm, e);
|
||||
set_owning_module(*enm, e);
|
||||
|
||||
if (e.skip())
|
||||
return true;
|
||||
@@ -265,6 +266,7 @@ bool translation_unit_visitor::VisitTypeAliasTemplateDecl(
|
||||
LOG_DBG("Adding class {} with id {}", name, id);
|
||||
|
||||
set_source_location(*cls, *template_specialization_ptr);
|
||||
set_owning_module(*cls, *template_specialization_ptr);
|
||||
|
||||
add_class(std::move(template_specialization_ptr));
|
||||
}
|
||||
@@ -677,6 +679,9 @@ bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls)
|
||||
|
||||
LOG_DBG(
|
||||
"== getQualifiedNameAsString() = {}", cls->getQualifiedNameAsString());
|
||||
if (cls->getOwningModule() != nullptr)
|
||||
LOG_DBG(
|
||||
"== getOwningModule()->Name = {}", cls->getOwningModule()->Name);
|
||||
LOG_DBG("== getID() = {}", cls->getID());
|
||||
LOG_DBG("== isTemplateDecl() = {}", cls->isTemplateDecl());
|
||||
LOG_DBG("== isTemplated() = {}", cls->isTemplated());
|
||||
@@ -762,6 +767,7 @@ translation_unit_visitor::create_concept_declaration(clang::ConceptDecl *cpt)
|
||||
|
||||
process_comment(*cpt, concept_model);
|
||||
set_source_location(*cpt, concept_model);
|
||||
set_owning_module(*cpt, concept_model);
|
||||
|
||||
if (concept_model.skip())
|
||||
return {};
|
||||
@@ -802,6 +808,7 @@ std::unique_ptr<class_> translation_unit_visitor::create_record_declaration(
|
||||
|
||||
process_comment(*rec, record);
|
||||
set_source_location(*rec, record);
|
||||
set_owning_module(*rec, record);
|
||||
|
||||
const auto record_full_name = record_ptr->full_name(false);
|
||||
|
||||
@@ -841,6 +848,7 @@ std::unique_ptr<class_> translation_unit_visitor::create_class_declaration(
|
||||
|
||||
process_comment(*cls, c);
|
||||
set_source_location(*cls, c);
|
||||
set_owning_module(*cls, c);
|
||||
|
||||
if (c.skip())
|
||||
return {};
|
||||
@@ -1835,6 +1843,7 @@ translation_unit_visitor::process_template_specialization(
|
||||
|
||||
process_comment(*cls, template_instantiation);
|
||||
set_source_location(*cls, template_instantiation);
|
||||
set_owning_module(*cls, template_instantiation);
|
||||
|
||||
if (template_instantiation.skip())
|
||||
return {};
|
||||
|
||||
@@ -285,6 +285,39 @@ tvl::value_t namespace_filter::match(const diagram &d, const element &e) const
|
||||
return result;
|
||||
}
|
||||
|
||||
modules_filter::modules_filter(
|
||||
filter_t type, std::vector<common::string_or_regex> modules)
|
||||
: filter_visitor{type}
|
||||
, modules_{std::move(modules)}
|
||||
{
|
||||
}
|
||||
|
||||
tvl::value_t modules_filter::match(const diagram &d, const element &e) const
|
||||
{
|
||||
if (modules_.empty())
|
||||
return {};
|
||||
|
||||
if (!e.module().has_value())
|
||||
return {false};
|
||||
|
||||
const auto module_toks = util::split(e.module().value(), ".");
|
||||
|
||||
auto result = tvl::any_of(modules_.begin(), modules_.end(),
|
||||
[&e, &module_toks](const auto &modit) {
|
||||
if (std::holds_alternative<std::string>(modit.value())) {
|
||||
const auto &modit_str = std::get<std::string>(modit.value());
|
||||
const auto modit_toks = util::split(modit_str, ".");
|
||||
|
||||
return e.module() == modit_str ||
|
||||
util::starts_with(module_toks, modit_toks);
|
||||
}
|
||||
|
||||
return std::get<common::regex>(modit.value()) %= e.module().value();
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
element_filter::element_filter(
|
||||
filter_t type, std::vector<common::string_or_regex> elements)
|
||||
: filter_visitor{type}
|
||||
@@ -887,6 +920,9 @@ void diagram_filter::init_filters(const config::diagram &c)
|
||||
add_inclusive_filter(std::make_unique<namespace_filter>(
|
||||
filter_t::kInclusive, c.include().namespaces));
|
||||
|
||||
add_inclusive_filter(std::make_unique<modules_filter>(
|
||||
filter_t::kInclusive, c.include().modules));
|
||||
|
||||
add_inclusive_filter(std::make_unique<relationship_filter>(
|
||||
filter_t::kInclusive, c.include().relationships));
|
||||
|
||||
@@ -997,6 +1033,9 @@ void diagram_filter::init_filters(const config::diagram &c)
|
||||
add_exclusive_filter(std::make_unique<namespace_filter>(
|
||||
filter_t::kExclusive, c.exclude().namespaces));
|
||||
|
||||
add_exclusive_filter(std::make_unique<modules_filter>(
|
||||
filter_t::kExclusive, c.exclude().modules));
|
||||
|
||||
add_exclusive_filter(std::make_unique<paths_filter>(
|
||||
filter_t::kExclusive, c.root_directory(), c.exclude().paths));
|
||||
|
||||
|
||||
@@ -154,6 +154,21 @@ private:
|
||||
std::vector<common::namespace_or_regex> namespaces_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Match diagram elements to a set of specified modules or
|
||||
* module regex patterns.
|
||||
*/
|
||||
struct modules_filter : public filter_visitor {
|
||||
modules_filter(filter_t type, std::vector<common::string_or_regex> modules);
|
||||
|
||||
~modules_filter() override = default;
|
||||
|
||||
tvl::value_t match(const diagram &d, const element &e) const override;
|
||||
|
||||
private:
|
||||
std::vector<common::string_or_regex> modules_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Match element's name to a set of names or regex patterns.
|
||||
*/
|
||||
|
||||
@@ -87,6 +87,20 @@ public:
|
||||
*/
|
||||
const namespace_ &path() const { return ns_; }
|
||||
|
||||
/**
|
||||
* Set elements owning module.
|
||||
*
|
||||
* @param module C++20 module.
|
||||
*/
|
||||
void set_module(const std::string &module) { module_ = module; }
|
||||
|
||||
/**
|
||||
* Return elements owning module, if any.
|
||||
*
|
||||
* @return C++20 module.
|
||||
*/
|
||||
std::optional<std::string> module() const { return module_; }
|
||||
|
||||
/**
|
||||
* Return elements full name.
|
||||
*
|
||||
@@ -120,5 +134,6 @@ public:
|
||||
private:
|
||||
namespace_ ns_;
|
||||
namespace_ using_namespace_;
|
||||
std::optional<std::string> module_;
|
||||
};
|
||||
} // namespace clanguml::common::model
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "comment/clang_visitor.h"
|
||||
#include "comment/plain_visitor.h"
|
||||
|
||||
#include "clang/Basic/Module.h"
|
||||
|
||||
namespace clanguml::common::visitor {
|
||||
|
||||
translation_unit_visitor::translation_unit_visitor(
|
||||
@@ -161,4 +163,12 @@ void translation_unit_visitor::set_source_location(
|
||||
element.set_location_id(location.getHashValue());
|
||||
}
|
||||
|
||||
void translation_unit_visitor::set_owning_module(
|
||||
const clang::Decl &decl, clanguml::common::model::element &element)
|
||||
{
|
||||
if (const clang::Module *module = decl.getOwningModule();
|
||||
module != nullptr) {
|
||||
element.set_module(module->Name);
|
||||
}
|
||||
}
|
||||
} // namespace clanguml::common::visitor
|
||||
@@ -100,6 +100,9 @@ public:
|
||||
void set_source_location(const clang::SourceLocation &location,
|
||||
clanguml::common::model::source_location &element);
|
||||
|
||||
void set_owning_module(
|
||||
const clang::Decl &decl, clanguml::common::model::element &element);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Process comment directives in comment attached to a declaration
|
||||
|
||||
@@ -180,6 +180,19 @@ struct filter {
|
||||
*/
|
||||
std::vector<common::namespace_or_regex> namespaces;
|
||||
|
||||
/*! @brief Modules filter
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```yaml
|
||||
* include
|
||||
* modules:
|
||||
* - app.module1
|
||||
* - r: ".*internal.*"
|
||||
* ```
|
||||
*/
|
||||
std::vector<common::string_or_regex> modules;
|
||||
|
||||
/*! @brief Elements filter
|
||||
*
|
||||
* Example:
|
||||
|
||||
@@ -64,10 +64,6 @@ types:
|
||||
regex_t:
|
||||
r: string
|
||||
regex_or_string_t: [string, regex_t]
|
||||
namespaces_filter_t:
|
||||
namespaces: [regex_or_string_t]
|
||||
elements_filter_t:
|
||||
elements: [regex_or_string_t]
|
||||
element_types_filter_t: !variant
|
||||
- class
|
||||
- enum
|
||||
@@ -121,6 +117,7 @@ types:
|
||||
- context_filter_match_t
|
||||
filter_t:
|
||||
namespaces: !optional [regex_or_string_t]
|
||||
modules: !optional [regex_or_string_t]
|
||||
elements: !optional [regex_or_string_t]
|
||||
element_types: !optional [element_types_filter_t]
|
||||
relationships: !optional [relationship_filter_t]
|
||||
|
||||
@@ -475,6 +475,12 @@ template <> struct convert<filter> {
|
||||
rhs.namespaces.push_back({ns});
|
||||
}
|
||||
|
||||
if (node["modules"]) {
|
||||
auto module_list = node["modules"].as<decltype(rhs.modules)>();
|
||||
for (const auto &ns : module_list)
|
||||
rhs.modules.push_back({ns});
|
||||
}
|
||||
|
||||
if (node["relationships"])
|
||||
rhs.relationships =
|
||||
node["relationships"].as<decltype(rhs.relationships)>();
|
||||
|
||||
@@ -122,6 +122,8 @@ YAML::Emitter &operator<<(YAML::Emitter &out, const filter &f)
|
||||
out << YAML::BeginMap;
|
||||
if (!f.namespaces.empty())
|
||||
out << YAML::Key << "namespaces" << YAML::Value << f.namespaces;
|
||||
if (!f.modules.empty())
|
||||
out << YAML::Key << "modules" << YAML::Value << f.modules;
|
||||
if (!f.access.empty())
|
||||
out << YAML::Key << "access" << YAML::Value << f.access;
|
||||
if (!f.context.empty())
|
||||
|
||||
Reference in New Issue
Block a user