Fixed package diagram generation
This commit is contained in:
@@ -25,13 +25,13 @@
|
||||
|
||||
namespace clanguml::class_diagram::model {
|
||||
|
||||
const std::vector<std::reference_wrapper<const class_>> &
|
||||
const std::vector<std::reference_wrapper<class_>> &
|
||||
diagram::classes() const
|
||||
{
|
||||
return classes_;
|
||||
}
|
||||
|
||||
const std::vector<std::reference_wrapper<const enum_>> &diagram::enums() const
|
||||
const std::vector<std::reference_wrapper<enum_>> &diagram::enums() const
|
||||
{
|
||||
return enums_;
|
||||
}
|
||||
@@ -41,14 +41,11 @@ common::model::diagram_t diagram::type() const
|
||||
return common::model::diagram_t::kClass;
|
||||
}
|
||||
|
||||
std::optional<
|
||||
std::reference_wrapper<const clanguml::common::model::diagram_element>>
|
||||
std::optional<std::reference_wrapper<clanguml::common::model::diagram_element>>
|
||||
diagram::get(const std::string &full_name) const
|
||||
{
|
||||
// type_safe::optional_ref<const clanguml::common::model::diagram_element>
|
||||
// res;
|
||||
std::optional<
|
||||
std::reference_wrapper<const clanguml::common::model::diagram_element>>
|
||||
std::reference_wrapper<clanguml::common::model::diagram_element>>
|
||||
res;
|
||||
|
||||
res = get_class(full_name);
|
||||
@@ -61,6 +58,23 @@ diagram::get(const std::string &full_name) const
|
||||
return res;
|
||||
}
|
||||
|
||||
std::optional<std::reference_wrapper<clanguml::common::model::diagram_element>>
|
||||
diagram::get(const clanguml::common::model::diagram_element::id_t id) const
|
||||
{
|
||||
std::optional<
|
||||
std::reference_wrapper<clanguml::common::model::diagram_element>>
|
||||
res;
|
||||
|
||||
res = get_class(id);
|
||||
|
||||
if (res.has_value())
|
||||
return res;
|
||||
|
||||
res = get_enum(id);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool diagram::has_class(const class_ &c) const
|
||||
{
|
||||
return std::any_of(classes_.cbegin(), classes_.cend(),
|
||||
@@ -73,7 +87,7 @@ bool diagram::has_enum(const enum_ &e) const
|
||||
[&e](const auto &ee) { return ee.get().full_name() == e.full_name(); });
|
||||
}
|
||||
|
||||
std::optional<std::reference_wrapper<const class_>> diagram::get_class(
|
||||
std::optional<std::reference_wrapper<class_>> diagram::get_class(
|
||||
const std::string &name) const
|
||||
{
|
||||
for (const auto &c : classes_) {
|
||||
@@ -87,7 +101,7 @@ std::optional<std::reference_wrapper<const class_>> diagram::get_class(
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<std::reference_wrapper<const class_>> diagram::get_class(
|
||||
std::optional<std::reference_wrapper<class_>> diagram::get_class(
|
||||
clanguml::common::model::diagram_element::id_t id) const
|
||||
{
|
||||
for (const auto &c : classes_) {
|
||||
@@ -99,7 +113,7 @@ std::optional<std::reference_wrapper<const class_>> diagram::get_class(
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<std::reference_wrapper<const enum_>> diagram::get_enum(
|
||||
std::optional<std::reference_wrapper<enum_>> diagram::get_enum(
|
||||
const std::string &name) const
|
||||
{
|
||||
for (const auto &e : enums_) {
|
||||
@@ -111,6 +125,18 @@ std::optional<std::reference_wrapper<const enum_>> diagram::get_enum(
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<std::reference_wrapper<enum_>> diagram::get_enum(
|
||||
clanguml::common::model::diagram_element::id_t id) const
|
||||
{
|
||||
for (const auto &e : enums_) {
|
||||
if (e.get().id() == id) {
|
||||
return {e};
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void diagram::add_type_alias(std::unique_ptr<type_alias> &&ta)
|
||||
{
|
||||
LOG_DBG(
|
||||
@@ -149,7 +175,7 @@ bool diagram::add_class(std::unique_ptr<class_> &&c)
|
||||
auto name = base_name;
|
||||
auto name_with_ns = c->name_and_ns();
|
||||
auto name_and_ns = ns | name;
|
||||
const auto &cc = *c;
|
||||
auto &cc = *c;
|
||||
|
||||
auto cc_ref = std::ref(cc);
|
||||
|
||||
|
||||
@@ -46,26 +46,33 @@ public:
|
||||
common::model::diagram_t type() const override;
|
||||
|
||||
std::optional<
|
||||
std::reference_wrapper<const clanguml::common::model::diagram_element>>
|
||||
std::reference_wrapper<clanguml::common::model::diagram_element>>
|
||||
get(const std::string &full_name) const override;
|
||||
|
||||
const std::vector<std::reference_wrapper<const class_>> &classes() const;
|
||||
std::optional<
|
||||
std::reference_wrapper<clanguml::common::model::diagram_element>>
|
||||
get(const clanguml::common::model::diagram_element::id_t id) const override;
|
||||
|
||||
const std::vector<std::reference_wrapper<const enum_>> &enums() const;
|
||||
const std::vector<std::reference_wrapper<class_>> &classes() const;
|
||||
|
||||
const std::vector<std::reference_wrapper<enum_>> &enums() const;
|
||||
|
||||
bool has_class(const class_ &c) const;
|
||||
|
||||
bool has_enum(const enum_ &e) const;
|
||||
|
||||
std::optional<std::reference_wrapper<const class_>> get_class(
|
||||
std::optional<std::reference_wrapper<class_>> get_class(
|
||||
const std::string &name) const;
|
||||
|
||||
std::optional<std::reference_wrapper<const class_>> get_class(
|
||||
std::optional<std::reference_wrapper<class_>> get_class(
|
||||
clanguml::common::model::diagram_element::id_t id) const;
|
||||
|
||||
std::optional<std::reference_wrapper<const enum_>> get_enum(
|
||||
std::optional<std::reference_wrapper<enum_>> get_enum(
|
||||
const std::string &name) const;
|
||||
|
||||
std::optional<std::reference_wrapper<enum_>> get_enum(
|
||||
clanguml::common::model::diagram_element::id_t id) const;
|
||||
|
||||
void add_type_alias(std::unique_ptr<type_alias> &&ta);
|
||||
|
||||
bool add_class(std::unique_ptr<class_> &&c);
|
||||
@@ -85,11 +92,10 @@ public:
|
||||
const clanguml::common::model::diagram_element::id_t id) const override;
|
||||
|
||||
private:
|
||||
std::vector<std::reference_wrapper<const class_>> classes_;
|
||||
std::vector<std::reference_wrapper<const enum_>> enums_;
|
||||
std::vector<std::reference_wrapper<class_>> classes_;
|
||||
|
||||
std::vector<std::reference_wrapper<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_;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -783,8 +783,7 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type,
|
||||
found_relationships_t &relationships,
|
||||
clanguml::common::model::relationship_t relationship_hint)
|
||||
{
|
||||
bool result = false;
|
||||
// std::string type_name =
|
||||
bool result{false};
|
||||
|
||||
if (type->isPointerType()) {
|
||||
relationship_hint = relationship_t::kAssociation;
|
||||
|
||||
@@ -58,13 +58,10 @@ public:
|
||||
|
||||
virtual bool VisitTypeAliasTemplateDecl(clang::TypeAliasTemplateDecl *cls);
|
||||
|
||||
// virtual bool VisitVarDecl(clang::VarDecl *variable_declaration);
|
||||
clanguml::class_diagram::model::diagram &diagram() { return diagram_; }
|
||||
|
||||
const clanguml::config::class_diagram &config() const { return config_; }
|
||||
|
||||
// void operator()();
|
||||
|
||||
void finalize();
|
||||
|
||||
private:
|
||||
|
||||
@@ -34,14 +34,19 @@ class relationship;
|
||||
class diagram {
|
||||
public:
|
||||
diagram();
|
||||
|
||||
virtual ~diagram();
|
||||
|
||||
virtual diagram_t type() const = 0;
|
||||
|
||||
virtual std::optional<
|
||||
std::reference_wrapper<const clanguml::common::model::diagram_element>>
|
||||
std::reference_wrapper<clanguml::common::model::diagram_element>>
|
||||
get(const std::string &full_name) const = 0;
|
||||
|
||||
virtual std::optional<
|
||||
std::reference_wrapper<clanguml::common::model::diagram_element>>
|
||||
get(const diagram_element::id_t id) const = 0;
|
||||
|
||||
diagram(const diagram &) = delete;
|
||||
diagram(diagram &&);
|
||||
diagram &operator=(const diagram &) = delete;
|
||||
@@ -62,6 +67,7 @@ public:
|
||||
bool should_include(const relationship r) const;
|
||||
bool should_include(const relationship_t r) const;
|
||||
bool should_include(const access_t s) const;
|
||||
|
||||
virtual bool has_element(const diagram_element::id_t id) const
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -63,14 +63,14 @@ const clanguml::common::optional_ref<class_diagram::model::class_> get(
|
||||
}
|
||||
|
||||
template <>
|
||||
const clanguml::common::optional_ref<const common::model::package> get(
|
||||
const clanguml::common::optional_ref<common::model::package> get(
|
||||
const package_diagram::model::diagram &d, const std::string &full_name)
|
||||
{
|
||||
return d.get_package(full_name);
|
||||
}
|
||||
|
||||
template <>
|
||||
const clanguml::common::optional_ref<const common::model::source_file> get(
|
||||
const clanguml::common::optional_ref<common::model::source_file> get(
|
||||
const include_diagram::model::diagram &d, const std::string &full_name)
|
||||
{
|
||||
return d.get_file(full_name);
|
||||
|
||||
@@ -27,10 +27,10 @@ namespace clanguml::common {
|
||||
using id_t = int64_t;
|
||||
|
||||
template <typename T>
|
||||
using optional_ref = std::optional<std::reference_wrapper<const T>>;
|
||||
using optional_ref = std::optional<std::reference_wrapper<T>>;
|
||||
|
||||
template <typename T>
|
||||
using reference_vector = std::vector<std::reference_wrapper<const T>>;
|
||||
using reference_vector = std::vector<std::reference_wrapper<T>>;
|
||||
|
||||
template <typename T>
|
||||
using reference_set = std::unordered_set<std::reference_wrapper<const T>>;
|
||||
|
||||
@@ -34,6 +34,12 @@ common::optional_ref<common::model::diagram_element> diagram::get(
|
||||
return get_file(full_name);
|
||||
}
|
||||
|
||||
common::optional_ref<common::model::diagram_element> diagram::get(
|
||||
const common::model::diagram_element::id_t /*id*/) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void diagram::add_file(std::unique_ptr<common::model::source_file> &&f)
|
||||
{
|
||||
LOG_DBG("Adding source file: {}, {}", f->name(), f->full_name(true));
|
||||
@@ -94,7 +100,7 @@ std::string diagram::to_alias(const std::string &full_name) const
|
||||
return source_file.value().alias();
|
||||
}
|
||||
|
||||
const common::reference_vector<const common::model::source_file> &
|
||||
const common::reference_vector<common::model::source_file> &
|
||||
diagram::files() const
|
||||
{
|
||||
return files_;
|
||||
|
||||
@@ -44,6 +44,9 @@ public:
|
||||
common::optional_ref<common::model::diagram_element> get(
|
||||
const std::string &full_name) const override;
|
||||
|
||||
common::optional_ref<common::model::diagram_element> get(
|
||||
const common::model::diagram_element::id_t id) const override;
|
||||
|
||||
void add_file(std::unique_ptr<common::model::source_file> &&f);
|
||||
|
||||
common::optional_ref<common::model::source_file> get_file(
|
||||
@@ -51,11 +54,11 @@ public:
|
||||
|
||||
std::string to_alias(const std::string &full_name) const;
|
||||
|
||||
const common::reference_vector<const common::model::source_file> &
|
||||
const common::reference_vector<common::model::source_file> &
|
||||
files() const;
|
||||
|
||||
private:
|
||||
common::reference_vector<const common::model::source_file> files_;
|
||||
common::reference_vector<common::model::source_file> files_;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ void generator::generate_relationships(
|
||||
try {
|
||||
relstr << p.alias()
|
||||
<< " ..> "
|
||||
//<< m_model.get_ to_alias(uns.relative(r.destination()))
|
||||
<< m_model.to_alias(r.destination())
|
||||
<< '\n';
|
||||
ostr << relstr.str();
|
||||
}
|
||||
|
||||
@@ -28,8 +28,7 @@ common::model::diagram_t diagram::type() const
|
||||
return common::model::diagram_t::kPackage;
|
||||
}
|
||||
|
||||
const std::vector<
|
||||
std::reference_wrapper<const clanguml::common::model::package>> &
|
||||
const std::vector<std::reference_wrapper<clanguml::common::model::package>> &
|
||||
diagram::packages() const
|
||||
{
|
||||
return packages_;
|
||||
@@ -46,7 +45,7 @@ void diagram::add_package(std::unique_ptr<common::model::package> &&p)
|
||||
add_element(ns, std::move(p));
|
||||
}
|
||||
|
||||
std::optional<std::reference_wrapper<const common::model::package>>
|
||||
std::optional<std::reference_wrapper<common::model::package>>
|
||||
diagram::get_package(const std::string &name) const
|
||||
{
|
||||
for (const auto &p : packages_) {
|
||||
@@ -59,30 +58,42 @@ diagram::get_package(const std::string &name) const
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<
|
||||
std::reference_wrapper<const clanguml::common::model::diagram_element>>
|
||||
std::optional<std::reference_wrapper<common::model::package>>
|
||||
diagram::get_package(
|
||||
const clanguml::common::model::diagram_element::id_t id) const
|
||||
{
|
||||
for (const auto &p : packages_) {
|
||||
if (p.get().id() == id) {
|
||||
return {p};
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<std::reference_wrapper<clanguml::common::model::diagram_element>>
|
||||
diagram::get(const std::string &full_name) const
|
||||
{
|
||||
return get_package(full_name);
|
||||
}
|
||||
|
||||
std::string diagram::to_alias(const std::string &full_name) const
|
||||
std::optional<std::reference_wrapper<clanguml::common::model::diagram_element>>
|
||||
diagram::get(const clanguml::common::model::diagram_element::id_t id) const
|
||||
{
|
||||
LOG_DBG("Looking for alias for {}", full_name);
|
||||
return get_package(id);
|
||||
}
|
||||
|
||||
auto path = common::model::namespace_{full_name};
|
||||
std::string diagram::to_alias(
|
||||
const clanguml::common::model::diagram_element::id_t id) const
|
||||
{
|
||||
LOG_DBG("Looking for alias for {}", id);
|
||||
|
||||
if (path.is_empty())
|
||||
throw error::uml_alias_missing(
|
||||
fmt::format("Missing alias for '{}'", path.to_string()));
|
||||
for (const auto &p : packages_) {
|
||||
if (p.get().id() == id)
|
||||
return p.get().alias();
|
||||
}
|
||||
|
||||
auto package = get_element<common::model::package>(path);
|
||||
|
||||
if (!package)
|
||||
throw error::uml_alias_missing(
|
||||
fmt::format("Missing alias for '{}'", path.to_string()));
|
||||
|
||||
return package.value().alias();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,24 +39,30 @@ public:
|
||||
|
||||
common::model::diagram_t type() const override;
|
||||
|
||||
const std::vector<
|
||||
std::reference_wrapper<const clanguml::common::model::package>> &
|
||||
packages() const;
|
||||
const std::vector<std::reference_wrapper<clanguml::common::model::package>>
|
||||
&packages() const;
|
||||
|
||||
std::optional<
|
||||
std::reference_wrapper<const clanguml::common::model::diagram_element>>
|
||||
std::reference_wrapper<clanguml::common::model::diagram_element>>
|
||||
get(const std::string &full_name) const override;
|
||||
|
||||
std::optional<
|
||||
std::reference_wrapper<clanguml::common::model::diagram_element>>
|
||||
get(const clanguml::common::model::diagram_element::id_t id) const override;
|
||||
|
||||
void add_package(std::unique_ptr<common::model::package> &&p);
|
||||
|
||||
std::optional<
|
||||
std::reference_wrapper<const clanguml::common::model::package>>
|
||||
std::optional<std::reference_wrapper<clanguml::common::model::package>>
|
||||
get_package(const std::string &name) const;
|
||||
|
||||
std::string to_alias(const std::string &full_name) const;
|
||||
std::optional<std::reference_wrapper<common::model::package>> get_package(
|
||||
const clanguml::common::model::diagram_element::id_t id) const;
|
||||
|
||||
std::string to_alias(
|
||||
const clanguml::common::model::diagram_element::id_t) const;
|
||||
|
||||
private:
|
||||
std::vector<std::reference_wrapper<const clanguml::common::model::package>>
|
||||
std::vector<std::reference_wrapper<clanguml::common::model::package>>
|
||||
packages_;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,11 +29,21 @@ namespace clanguml::package_diagram::visitor {
|
||||
|
||||
using clanguml::class_diagram::model::type_alias;
|
||||
using clanguml::common::model::access_t;
|
||||
using clanguml::common::model::namespace_;
|
||||
using clanguml::common::model::package;
|
||||
using clanguml::common::model::relationship;
|
||||
using clanguml::common::model::relationship_t;
|
||||
using clanguml::package_diagram::model::diagram;
|
||||
|
||||
int64_t to_id(const clang::NamespaceDecl *ns)
|
||||
{
|
||||
auto qualified_name = ns->getQualifiedNameAsString();
|
||||
util::replace_all(qualified_name, "(anonymous namespace)", "");
|
||||
util::replace_all(qualified_name, "::::", "::");
|
||||
|
||||
return std::hash<std::string>{}(qualified_name) >> 3;
|
||||
}
|
||||
|
||||
translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm,
|
||||
clanguml::package_diagram::model::diagram &diagram,
|
||||
const clanguml::config::package_diagram &config)
|
||||
@@ -43,4 +53,362 @@ translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm,
|
||||
{
|
||||
}
|
||||
|
||||
bool translation_unit_visitor::VisitNamespaceDecl(clang::NamespaceDecl *ns)
|
||||
{
|
||||
if (ns->isAnonymousNamespace() || ns->isInline())
|
||||
return true;
|
||||
|
||||
auto qualified_name = ns->getQualifiedNameAsString();
|
||||
util::replace_all(qualified_name, "(anonymous namespace)", "");
|
||||
util::replace_all(qualified_name, "::::", "::");
|
||||
|
||||
LOG_DBG("Visiting namespace declaration: {}", qualified_name);
|
||||
|
||||
auto package_path = namespace_{qualified_name};
|
||||
auto package_parent = package_path;
|
||||
|
||||
std::string name;
|
||||
if (!package_path.is_empty())
|
||||
name = package_path.name();
|
||||
|
||||
if (!package_parent.is_empty())
|
||||
package_parent.pop_back();
|
||||
|
||||
const auto usn = config().using_namespace();
|
||||
|
||||
auto p = std::make_unique<common::model::package>(usn);
|
||||
package_path = package_path.relative_to(usn);
|
||||
|
||||
p->set_name(name);
|
||||
p->set_namespace(package_parent);
|
||||
p->set_id(to_id(ns));
|
||||
|
||||
assert(p->id() > 0);
|
||||
|
||||
if (diagram().should_include(*p) && !diagram().get(p->id())) {
|
||||
process_comment(*ns, *p);
|
||||
set_source_location(*ns, *p);
|
||||
|
||||
p->set_style(p->style_spec());
|
||||
|
||||
for (const auto *attr : ns->attrs()) {
|
||||
if (attr->getKind() == clang::attr::Kind::Deprecated) {
|
||||
p->set_deprecated(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!p->skip()) {
|
||||
diagram().add_package(std::move(p));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool translation_unit_visitor::VisitFunctionDecl(
|
||||
clang::FunctionDecl *function_declaration)
|
||||
{
|
||||
assert(function_declaration != nullptr);
|
||||
|
||||
// Skip system headers
|
||||
if (source_manager_.isInSystemHeader(
|
||||
function_declaration->getSourceRange().getBegin()))
|
||||
return true;
|
||||
|
||||
found_relationships_t relationships;
|
||||
|
||||
find_relationships(function_declaration->getReturnType(), relationships);
|
||||
|
||||
for (const auto *param : function_declaration->parameters()) {
|
||||
if (param != nullptr)
|
||||
find_relationships(param->getType(), relationships);
|
||||
}
|
||||
|
||||
add_relationships(function_declaration, relationships);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls)
|
||||
{
|
||||
assert(cls != nullptr);
|
||||
|
||||
// Skip system headers
|
||||
if (source_manager_.isInSystemHeader(cls->getSourceRange().getBegin()))
|
||||
return true;
|
||||
|
||||
// Templated records are handled by VisitClassTemplateDecl()
|
||||
if (cls->isTemplated() || cls->isTemplateDecl() ||
|
||||
(clang::dyn_cast_or_null<clang::ClassTemplateSpecializationDecl>(cls) !=
|
||||
nullptr))
|
||||
return true;
|
||||
|
||||
found_relationships_t relationships;
|
||||
|
||||
process_class_declaration(*cls, relationships);
|
||||
|
||||
add_relationships(cls, relationships);
|
||||
|
||||
return true;
|
||||
}
|
||||
void translation_unit_visitor::add_relationships(
|
||||
clang::DeclContext *cls, found_relationships_t &relationships)
|
||||
{
|
||||
int64_t current_package_id{0};
|
||||
|
||||
const auto *namespace_context = cls->getEnclosingNamespaceContext();
|
||||
if (namespace_context != nullptr && namespace_context->isNamespace()) {
|
||||
current_package_id =
|
||||
to_id(llvm::cast<clang::NamespaceDecl>(namespace_context));
|
||||
}
|
||||
|
||||
assert(current_package_id != 0);
|
||||
|
||||
auto current_package = diagram().get(current_package_id);
|
||||
|
||||
if (current_package) {
|
||||
for (const auto &dependency : relationships) {
|
||||
const auto destination_id = std::get<0>(dependency);
|
||||
relationship r{relationship_t::kDependency, destination_id};
|
||||
if (destination_id != current_package_id)
|
||||
current_package.value().get().add_relationship(std::move(r));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void translation_unit_visitor::process_class_declaration(
|
||||
const clang::CXXRecordDecl &cls, found_relationships_t &relationships)
|
||||
{
|
||||
// Look for dependency relationships in class children (fields, methods)
|
||||
process_class_children(cls, relationships);
|
||||
|
||||
// Look for dependency relationships in class bases
|
||||
process_class_bases(cls, relationships);
|
||||
}
|
||||
|
||||
void translation_unit_visitor::process_class_children(
|
||||
const clang::CXXRecordDecl &cls, found_relationships_t &relationships)
|
||||
{
|
||||
// Iterate over class methods (both regular and static)
|
||||
for (const auto *method : cls.methods()) {
|
||||
if (method != nullptr) {
|
||||
process_method(*method, relationships);
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over class template methods
|
||||
for (auto const *decl_iterator :
|
||||
clang::dyn_cast_or_null<clang::DeclContext>(&cls)->decls()) {
|
||||
auto const *method_template =
|
||||
llvm::dyn_cast_or_null<clang::FunctionTemplateDecl>(decl_iterator);
|
||||
if (method_template == nullptr)
|
||||
continue;
|
||||
|
||||
process_template_method(*method_template, relationships);
|
||||
}
|
||||
|
||||
// Iterate over regular class fields
|
||||
for (const auto *field : cls.fields()) {
|
||||
if (field != nullptr)
|
||||
process_field(*field, relationships);
|
||||
}
|
||||
|
||||
// Static fields have to be processed by iterating over variable
|
||||
// declarations
|
||||
for (const auto *decl : cls.decls()) {
|
||||
if (decl->getKind() == clang::Decl::Var) {
|
||||
const clang::VarDecl *variable_declaration{
|
||||
dynamic_cast<const clang::VarDecl *>(decl)};
|
||||
if (variable_declaration &&
|
||||
variable_declaration->isStaticDataMember()) {
|
||||
process_static_field(*variable_declaration, relationships);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cls.isCompleteDefinition())
|
||||
for (const auto *friend_declaration : cls.friends()) {
|
||||
if (friend_declaration != nullptr)
|
||||
process_friend(*friend_declaration, relationships);
|
||||
}
|
||||
}
|
||||
|
||||
void translation_unit_visitor::process_class_bases(
|
||||
const clang::CXXRecordDecl &cls, found_relationships_t &relationships)
|
||||
{
|
||||
for (auto &base : cls.bases()) {
|
||||
find_relationships(base.getType(), relationships);
|
||||
}
|
||||
}
|
||||
|
||||
void translation_unit_visitor::process_method(
|
||||
const clang::CXXMethodDecl &method, found_relationships_t &relationships)
|
||||
{
|
||||
find_relationships(method.getReturnType(), relationships);
|
||||
|
||||
for (const auto *param : method.parameters()) {
|
||||
if (param != nullptr)
|
||||
find_relationships(param->getType(), relationships);
|
||||
}
|
||||
}
|
||||
|
||||
void translation_unit_visitor::process_template_method(
|
||||
const clang::FunctionTemplateDecl &method,
|
||||
found_relationships_t &relationships)
|
||||
{
|
||||
// TODO: For now skip implicitly default methods
|
||||
// in the future, add config option to choose
|
||||
if (method.getTemplatedDecl()->isDefaulted() &&
|
||||
!method.getTemplatedDecl()->isExplicitlyDefaulted())
|
||||
return;
|
||||
|
||||
find_relationships(
|
||||
method.getTemplatedDecl()->getReturnType(), relationships);
|
||||
|
||||
for (const auto *param : method.getTemplatedDecl()->parameters()) {
|
||||
if (param != nullptr) {
|
||||
find_relationships(param->getType(), relationships);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void translation_unit_visitor::process_field(
|
||||
const clang::FieldDecl &field_declaration,
|
||||
found_relationships_t &relationships)
|
||||
{
|
||||
find_relationships(field_declaration.getType(), relationships,
|
||||
relationship_t::kDependency);
|
||||
}
|
||||
|
||||
void translation_unit_visitor::process_static_field(
|
||||
const clang::VarDecl &field_declaration,
|
||||
found_relationships_t &relationships)
|
||||
{
|
||||
find_relationships(field_declaration.getType(), relationships,
|
||||
relationship_t::kDependency);
|
||||
}
|
||||
|
||||
void translation_unit_visitor::process_friend(
|
||||
const clang::FriendDecl &friend_declaration,
|
||||
found_relationships_t &relationships)
|
||||
{
|
||||
if (const auto *friend_type_declaration =
|
||||
friend_declaration.getFriendDecl()) {
|
||||
if (friend_type_declaration->isTemplateDecl()) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
else if (const auto *friend_type = friend_declaration.getFriendType()) {
|
||||
find_relationships(friend_type->getType(), relationships);
|
||||
}
|
||||
}
|
||||
|
||||
bool translation_unit_visitor::find_relationships(const clang::QualType &type,
|
||||
found_relationships_t &relationships, relationship_t relationship_hint)
|
||||
{
|
||||
bool result{false};
|
||||
|
||||
if (type->isVoidType() || type->isVoidPointerType()) {
|
||||
// pass
|
||||
}
|
||||
else if (type->isPointerType()) {
|
||||
relationship_hint = relationship_t::kAssociation;
|
||||
find_relationships(
|
||||
type->getPointeeType(), relationships, relationship_hint);
|
||||
}
|
||||
else if (type->isRValueReferenceType()) {
|
||||
relationship_hint = relationship_t::kAggregation;
|
||||
find_relationships(
|
||||
type.getNonReferenceType(), relationships, relationship_hint);
|
||||
}
|
||||
else if (type->isLValueReferenceType()) {
|
||||
relationship_hint = relationship_t::kAssociation;
|
||||
find_relationships(
|
||||
type.getNonReferenceType(), relationships, relationship_hint);
|
||||
}
|
||||
else if (type->isArrayType()) {
|
||||
find_relationships(type->getAsArrayTypeUnsafe()->getElementType(),
|
||||
relationships, relationship_t::kAggregation);
|
||||
}
|
||||
else if (type->isEnumeralType()) {
|
||||
relationships.emplace_back(
|
||||
type->getAs<clang::EnumType>()->getDecl()->getID(),
|
||||
relationship_hint);
|
||||
}
|
||||
else if (auto *template_specialization_type =
|
||||
type->getAs<clang::TemplateSpecializationType>()) {
|
||||
if (template_specialization_type != nullptr) {
|
||||
if (template_specialization_type->isTypeAlias())
|
||||
template_specialization_type =
|
||||
template_specialization_type->getAliasedType()
|
||||
->getAs<clang::TemplateSpecializationType>();
|
||||
}
|
||||
|
||||
if (template_specialization_type != nullptr) {
|
||||
for (const auto &template_argument :
|
||||
*template_specialization_type) {
|
||||
const auto template_argument_kind = template_argument.getKind();
|
||||
if (template_argument_kind ==
|
||||
clang::TemplateArgument::ArgKind::Integral) {
|
||||
// pass
|
||||
}
|
||||
else if (template_argument_kind ==
|
||||
clang::TemplateArgument::ArgKind::Null) {
|
||||
// pass
|
||||
}
|
||||
else if (template_argument_kind ==
|
||||
clang::TemplateArgument::ArgKind::Expression) {
|
||||
// pass
|
||||
}
|
||||
else if (template_argument.getKind() ==
|
||||
clang::TemplateArgument::ArgKind::NullPtr) {
|
||||
// pass
|
||||
}
|
||||
else if (template_argument_kind ==
|
||||
clang::TemplateArgument::ArgKind::Template) {
|
||||
// pass
|
||||
}
|
||||
else if (template_argument_kind ==
|
||||
clang::TemplateArgument::ArgKind::TemplateExpansion) {
|
||||
// pass
|
||||
}
|
||||
else if (template_argument.getAsType()
|
||||
->getAs<clang::FunctionProtoType>()) {
|
||||
for (const auto ¶m_type :
|
||||
template_argument.getAsType()
|
||||
->getAs<clang::FunctionProtoType>()
|
||||
->param_types()) {
|
||||
result = find_relationships(param_type, relationships,
|
||||
relationship_t::kDependency);
|
||||
}
|
||||
}
|
||||
else if (template_argument_kind ==
|
||||
clang::TemplateArgument::ArgKind::Type) {
|
||||
result = find_relationships(template_argument.getAsType(),
|
||||
relationships, relationship_hint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type->isRecordType()) {
|
||||
const auto *namespace_context =
|
||||
type->getAsCXXRecordDecl()->getEnclosingNamespaceContext();
|
||||
if (namespace_context != nullptr && namespace_context->isNamespace()) {
|
||||
const auto *namespace_declaration =
|
||||
clang::cast<clang::NamespaceDecl>(namespace_context);
|
||||
|
||||
if (diagram().should_include(
|
||||
namespace_declaration->getQualifiedNameAsString())) {
|
||||
const auto target_id =
|
||||
to_id(clang::cast<clang::NamespaceDecl>(namespace_context));
|
||||
relationships.emplace_back(target_id, relationship_hint);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,6 +33,10 @@
|
||||
|
||||
namespace clanguml::package_diagram::visitor {
|
||||
|
||||
using found_relationships_t =
|
||||
std::vector<std::pair<clanguml::common::model::diagram_element::id_t,
|
||||
common::model::relationship_t>>;
|
||||
|
||||
class translation_unit_visitor
|
||||
: public clang::RecursiveASTVisitor<translation_unit_visitor> {
|
||||
public:
|
||||
@@ -40,9 +44,75 @@ public:
|
||||
clanguml::package_diagram::model::diagram &diagram,
|
||||
const clanguml::config::package_diagram &config);
|
||||
|
||||
virtual bool VisitNamespaceDecl(clang::NamespaceDecl *ns);
|
||||
|
||||
virtual bool VisitCXXRecordDecl(clang::CXXRecordDecl *cls);
|
||||
|
||||
virtual bool VisitFunctionDecl(clang::FunctionDecl *function_declaration);
|
||||
|
||||
clanguml::package_diagram::model::diagram &diagram() { return diagram_; }
|
||||
|
||||
const clanguml::config::package_diagram &config() const { return config_; }
|
||||
|
||||
void finalize() { }
|
||||
|
||||
private:
|
||||
void process_class_declaration(
|
||||
const clang::CXXRecordDecl &cls, found_relationships_t &relationships);
|
||||
|
||||
void process_class_children(
|
||||
const clang::CXXRecordDecl &cls, found_relationships_t &relationships);
|
||||
|
||||
void process_class_bases(
|
||||
const clang::CXXRecordDecl &cls, found_relationships_t &relationships);
|
||||
|
||||
void process_method(const clang::CXXMethodDecl &method,
|
||||
found_relationships_t &relationships);
|
||||
|
||||
void process_template_method(const clang::FunctionTemplateDecl &method,
|
||||
found_relationships_t &relationships);
|
||||
|
||||
void process_field(const clang::FieldDecl &field_declaration,
|
||||
found_relationships_t &relationships);
|
||||
|
||||
void process_static_field(const clang::VarDecl &field_declaration,
|
||||
found_relationships_t &relationships);
|
||||
|
||||
void process_friend(const clang::FriendDecl &friend_declaration,
|
||||
found_relationships_t &relationships);
|
||||
|
||||
bool find_relationships(const clang::QualType &type,
|
||||
found_relationships_t &relationships,
|
||||
common::model::relationship_t relationship_hint =
|
||||
common::model::relationship_t::kDependency);
|
||||
|
||||
void add_relationships(clang::DeclContext *cls, found_relationships_t &relationships);
|
||||
|
||||
template <typename ClangDecl>
|
||||
void process_comment(
|
||||
const ClangDecl &decl, clanguml::common::model::decorated_element &e)
|
||||
{
|
||||
const auto *comment =
|
||||
decl.getASTContext().getRawCommentForDeclNoCache(&decl);
|
||||
|
||||
if (comment != nullptr) {
|
||||
e.set_comment(comment->getFormattedText(
|
||||
source_manager_, decl.getASTContext().getDiagnostics()));
|
||||
e.add_decorators(decorators::parse(e.comment().value()));
|
||||
}
|
||||
}
|
||||
|
||||
void set_source_location(const clang::Decl &decl,
|
||||
clanguml::common::model::source_location &element)
|
||||
{
|
||||
if (decl.getLocation().isValid()) {
|
||||
element.set_file(
|
||||
source_manager_.getFilename(decl.getLocation()).str());
|
||||
element.set_line(
|
||||
source_manager_.getSpellingLineNumber(decl.getLocation()));
|
||||
}
|
||||
}
|
||||
|
||||
clang::SourceManager &source_manager_;
|
||||
|
||||
// Reference to the output diagram model
|
||||
|
||||
@@ -34,6 +34,12 @@ common::optional_ref<common::model::diagram_element> diagram::get(
|
||||
return {};
|
||||
}
|
||||
|
||||
common::optional_ref<common::model::diagram_element> diagram::get(
|
||||
const common::model::diagram_element::id_t /*id*/) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string diagram::to_alias(const std::string &full_name) const
|
||||
{
|
||||
return full_name;
|
||||
|
||||
@@ -40,6 +40,9 @@ public:
|
||||
common::optional_ref<common::model::diagram_element> get(
|
||||
const std::string &full_name) const override;
|
||||
|
||||
common::optional_ref<common::model::diagram_element> get(
|
||||
const common::model::diagram_element::id_t id) const override;
|
||||
|
||||
std::string to_alias(const std::string &full_name) const;
|
||||
|
||||
bool started{false};
|
||||
|
||||
@@ -24,7 +24,7 @@ TEST_CASE("t30001", "[test-case][package]")
|
||||
|
||||
REQUIRE(diagram->name == "t30001_package");
|
||||
|
||||
auto model = generate_package_diagram(db, diagram);
|
||||
auto model = generate_package_diagram(*db, diagram);
|
||||
|
||||
REQUIRE(model->name() == "t30001_package");
|
||||
|
||||
|
||||
@@ -66,6 +66,14 @@ namespace A15 {
|
||||
struct CO {
|
||||
};
|
||||
}
|
||||
namespace A16 {
|
||||
struct CP {
|
||||
};
|
||||
}
|
||||
namespace A17 {
|
||||
struct CR {
|
||||
};
|
||||
}
|
||||
}
|
||||
namespace B::BB::BBB {
|
||||
class CBA : public A::AA::A6::CF {
|
||||
@@ -75,11 +83,14 @@ public:
|
||||
std::shared_ptr<A::AA::A3::CC> cc_;
|
||||
std::map<std::string, std::unique_ptr<A::AA::A4::CD>> *cd_;
|
||||
std::array<A::AA::A15::CO, 5> co_;
|
||||
static A::AA::A16::CP* cp_;
|
||||
|
||||
CBA() = default;
|
||||
|
||||
CBA(A::AA::A14::CN *cn) { }
|
||||
|
||||
friend A::AA::A17::CR;
|
||||
|
||||
template <typename... Item> CBA(std::tuple<Item...> &items) { }
|
||||
|
||||
void ce(const std::vector<A::AA::A5::CE> /*ce_*/) { }
|
||||
@@ -87,7 +98,7 @@ public:
|
||||
std::shared_ptr<A::AA::A7::CG> cg() { return {}; }
|
||||
|
||||
template <typename T>
|
||||
void ch(std::map<T, std::shared_ptr<A::AA::A8::CH>> & /*ch_*/)
|
||||
void ch(std::map<T, std::shared_ptr<A::AA::A8::CH>> & ch_)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ TEST_CASE("t30002", "[test-case][package]")
|
||||
|
||||
REQUIRE(diagram->name == "t30002_package");
|
||||
|
||||
auto model = generate_package_diagram(db, diagram);
|
||||
auto model = generate_package_diagram(*db, diagram);
|
||||
|
||||
REQUIRE(model->name() == "t30002_package");
|
||||
|
||||
@@ -48,6 +48,8 @@ TEST_CASE("t30002", "[test-case][package]")
|
||||
REQUIRE_THAT(puml, IsPackage("A13"));
|
||||
REQUIRE_THAT(puml, IsPackage("A14"));
|
||||
REQUIRE_THAT(puml, IsPackage("A15"));
|
||||
REQUIRE_THAT(puml, IsPackage("A16"));
|
||||
REQUIRE_THAT(puml, IsPackage("A17"));
|
||||
|
||||
REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("A1")));
|
||||
REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("A2")));
|
||||
@@ -64,6 +66,8 @@ TEST_CASE("t30002", "[test-case][package]")
|
||||
REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("A13")));
|
||||
REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("A14")));
|
||||
REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("A15")));
|
||||
REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("A16")));
|
||||
REQUIRE_THAT(puml, IsDependency(_A("BBB"), _A("A17")));
|
||||
|
||||
save_puml(
|
||||
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);
|
||||
|
||||
@@ -24,7 +24,7 @@ TEST_CASE("t30003", "[test-case][package]")
|
||||
|
||||
REQUIRE(diagram->name == "t30003_package");
|
||||
|
||||
auto model = generate_package_diagram(db, diagram);
|
||||
auto model = generate_package_diagram(*db, diagram);
|
||||
|
||||
REQUIRE(model->name() == "t30003_package");
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ TEST_CASE("t30004", "[test-case][package]")
|
||||
|
||||
REQUIRE(diagram->name == "t30004_package");
|
||||
|
||||
auto model = generate_package_diagram(db, diagram);
|
||||
auto model = generate_package_diagram(*db, diagram);
|
||||
|
||||
REQUIRE(model->name() == "t30004_package");
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ TEST_CASE("t30005", "[test-case][package]")
|
||||
|
||||
REQUIRE(diagram->name == "t30005_package");
|
||||
|
||||
auto model = generate_package_diagram(db, diagram);
|
||||
auto model = generate_package_diagram(*db, diagram);
|
||||
|
||||
REQUIRE(model->name() == "t30005_package");
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ TEST_CASE("t30006", "[test-case][package]")
|
||||
|
||||
REQUIRE(diagram->name == "t30006_package");
|
||||
|
||||
auto model = generate_package_diagram(db, diagram);
|
||||
auto model = generate_package_diagram(*db, diagram);
|
||||
|
||||
REQUIRE(model->name() == "t30006_package");
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ TEST_CASE("t30007", "[test-case][package]")
|
||||
|
||||
REQUIRE(diagram->name == "t30007_package");
|
||||
|
||||
auto model = generate_package_diagram(db, diagram);
|
||||
auto model = generate_package_diagram(*db, diagram);
|
||||
|
||||
REQUIRE(model->name() == "t30007_package");
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ TEST_CASE("t30008", "[test-case][package]")
|
||||
|
||||
REQUIRE(diagram->name == "t30008_package");
|
||||
|
||||
auto model = generate_package_diagram(db, diagram);
|
||||
auto model = generate_package_diagram(*db, diagram);
|
||||
|
||||
REQUIRE(model->name() == "t30008_package");
|
||||
|
||||
|
||||
@@ -245,14 +245,14 @@ using namespace clanguml::test::matchers;
|
||||
////
|
||||
//// Package diagram tests
|
||||
////
|
||||
//#include "t30001/test_case.h"
|
||||
//#include "t30002/test_case.h"
|
||||
//#include "t30003/test_case.h"
|
||||
//#include "t30004/test_case.h"
|
||||
//#include "t30005/test_case.h"
|
||||
//#include "t30006/test_case.h"
|
||||
//#include "t30007/test_case.h"
|
||||
//#include "t30008/test_case.h"
|
||||
#include "t30001/test_case.h"
|
||||
#include "t30002/test_case.h"
|
||||
#include "t30003/test_case.h"
|
||||
#include "t30004/test_case.h"
|
||||
#include "t30005/test_case.h"
|
||||
#include "t30006/test_case.h"
|
||||
#include "t30007/test_case.h"
|
||||
#include "t30008/test_case.h"
|
||||
//
|
||||
////
|
||||
//// Include diagram tests
|
||||
|
||||
Reference in New Issue
Block a user