From 75c027262fdd1d1aaf80a687b425d21b706345b5 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Wed, 24 May 2023 22:22:47 +0200 Subject: [PATCH] Fixed class diagram generation with packages from directories --- src/class_diagram/model/diagram.cc | 135 ++++++++++++++++-- src/class_diagram/model/diagram.h | 32 +++-- src/class_diagram/visitor/template_builder.cc | 21 +-- src/class_diagram/visitor/template_builder.h | 10 +- .../visitor/translation_unit_visitor.cc | 79 ++++++---- .../visitor/translation_unit_visitor.h | 11 +- src/common/visitor/translation_unit_visitor.h | 2 +- 7 files changed, 226 insertions(+), 64 deletions(-) diff --git a/src/class_diagram/model/diagram.cc b/src/class_diagram/model/diagram.cc index 6e80776f..1d3afdaf 100644 --- a/src/class_diagram/model/diagram.cc +++ b/src/class_diagram/model/diagram.cc @@ -173,16 +173,56 @@ common::optional_ref diagram::get_concept( return {}; } -bool diagram::add_package_fs(std::unique_ptr &&p) +bool diagram::add_class( + const common::model::path &parent_path, std::unique_ptr &&c) +{ + if (parent_path.type() == common::model::path_type::kNamespace) { + return add_class_ns(std::move(c)); + } + + return add_class_fs(parent_path, std::move(c)); +} + +bool diagram::add_enum(const path &parent_path, std::unique_ptr &&e) +{ + if (parent_path.type() == common::model::path_type::kNamespace) { + return add_enum_ns(std::move(e)); + } + + return add_enum_fs(parent_path, std::move(e)); +} + +bool diagram::add_concept( + const path &parent_path, std::unique_ptr &&c) +{ + if (parent_path.type() == common::model::path_type::kNamespace) { + return add_concept_ns(std::move(c)); + } + + return add_concept_fs(parent_path, std::move(c)); +} + +bool diagram::add_package( + const path &parent_path, std::unique_ptr &&p) +{ + if (parent_path.type() == common::model::path_type::kNamespace) { + return add_package_ns(std::move(p)); + } + + return add_package_fs(parent_path, std::move(p)); +} + +bool diagram::add_package_fs( + const path &parent_path, std::unique_ptr &&p) { LOG_DBG("Adding filesystem package: {}, {}", p->name(), p->full_name(true)); - auto ns = p->get_namespace(); + auto ns = p->get_relative_namespace(); return add_element(ns, std::move(p)); } -bool diagram::add_package(std::unique_ptr &&p) +bool diagram::add_package_ns(std::unique_ptr &&p) { LOG_DBG("Adding namespace package: {}, {}", p->name(), p->full_name(true)); @@ -192,14 +232,28 @@ bool diagram::add_package(std::unique_ptr &&p) } bool diagram::add_class_fs( - const common::model::path &p, std::unique_ptr &&c) + const common::model::path &parent_path, std::unique_ptr &&c) { + // Make sure all parent directories are already packages in the + // model + for (auto it = parent_path.begin(); it != parent_path.end(); it++) { + auto pkg = + std::make_unique(c->using_namespace()); + pkg->set_name(*it); + auto ns = common::model::path(parent_path.begin(), it); + // ns.pop_back(); + pkg->set_namespace(ns); + pkg->set_id(common::to_id(pkg->full_name(false))); + + add_package_fs(ns, std::move(pkg)); + } + const auto base_name = c->name(); const auto full_name = c->full_name(false); const auto id = c->id(); auto &cc = *c; - if (add_element(p, std::move(c))) { + if (add_element(parent_path, std::move(c))) { classes_.push_back(std::ref(cc)); return true; } @@ -210,7 +264,7 @@ bool diagram::add_class_fs( return false; } -bool diagram::add_class(std::unique_ptr &&c) +bool diagram::add_class_ns(std::unique_ptr &&c) { const auto base_name = c->name(); const auto full_name = c->full_name(false); @@ -259,7 +313,7 @@ bool diagram::add_class(std::unique_ptr &&c) return false; } -bool diagram::add_enum(std::unique_ptr &&e) +bool diagram::add_enum_ns(std::unique_ptr &&e) { const auto full_name = e->name(); @@ -282,7 +336,40 @@ bool diagram::add_enum(std::unique_ptr &&e) return false; } -bool diagram::add_concept(std::unique_ptr &&c) +bool diagram::add_enum_fs( + const common::model::path &parent_path, std::unique_ptr &&e) +{ + // Make sure all parent directories are already packages in the + // model + for (auto it = parent_path.begin(); it != parent_path.end(); it++) { + auto pkg = + std::make_unique(e->using_namespace()); + pkg->set_name(*it); + auto ns = common::model::path(parent_path.begin(), it); + // ns.pop_back(); + pkg->set_namespace(ns); + pkg->set_id(common::to_id(pkg->full_name(false))); + + add_package_fs(ns, std::move(pkg)); + } + + const auto base_name = e->name(); + const auto full_name = e->full_name(false); + const auto id = e->id(); + auto &cc = *e; + + if (add_element(parent_path, std::move(e))) { + enums_.push_back(std::ref(cc)); + return true; + } + else { + LOG_WARN("Cannot add class {} with id {} due to: {}", base_name, id); + } + + return false; +} + +bool diagram::add_concept_ns(std::unique_ptr &&c) { const auto base_name = c->name(); const auto full_name = c->full_name(false); @@ -331,6 +418,38 @@ bool diagram::add_concept(std::unique_ptr &&c) return false; } +bool diagram::add_concept_fs( + const common::model::path &parent_path, std::unique_ptr &&c) +{ + // Make sure all parent directories are already packages in the + // model + for (auto it = parent_path.begin(); it != parent_path.end(); it++) { + auto pkg = + std::make_unique(c->using_namespace()); + pkg->set_name(*it); + auto ns = common::model::path(parent_path.begin(), it); + // ns.pop_back(); + pkg->set_namespace(ns); + pkg->set_id(common::to_id(pkg->full_name(false))); + + add_package_fs(ns, std::move(pkg)); + } + + const auto base_name = c->name(); + const auto full_name = c->full_name(false); + const auto id = c->id(); + auto &cc = *c; + + if (add_element(parent_path, std::move(c))) { + concepts_.push_back(std::ref(cc)); + return true; + } + + LOG_WARN("Cannot add class {} with id {} due to: {}", base_name, id); + + return false; +} + void diagram::get_parents( clanguml::common::reference_set &parents) const { diff --git a/src/class_diagram/model/diagram.h b/src/class_diagram/model/diagram.h index 5077905b..1d566ac7 100644 --- a/src/class_diagram/model/diagram.h +++ b/src/class_diagram/model/diagram.h @@ -34,6 +34,8 @@ namespace clanguml::class_diagram::model { using common::opt_ref; using common::model::diagram_element; using common::model::diagram_t; +using common::model::path; +using common::model::path_type; using nested_trait_ns = clanguml::common::model::nested_trait get_concept(diagram_element::id_t id) const; - bool add_class_fs( - const common::model::path &p, std::unique_ptr &&c); + bool add_class(const path &parent_path, std::unique_ptr &&c); - bool add_class(std::unique_ptr &&c); + bool add_enum(const path &parent_path, std::unique_ptr &&e); - bool add_enum(std::unique_ptr &&e); + bool add_concept(const path &parent_path, std::unique_ptr &&e); - bool add_concept(std::unique_ptr &&e); - - bool add_package(std::unique_ptr &&p); - - bool add_package_fs(std::unique_ptr &&p); + bool add_package( + const path &parent_path, std::unique_ptr &&p); std::string to_alias(diagram_element::id_t id) const; @@ -102,6 +100,22 @@ public: inja::json context() const override; private: + bool add_class_ns(std::unique_ptr &&c); + bool add_class_fs( + const common::model::path &parent_path, std::unique_ptr &&c); + + bool add_enum_ns(std::unique_ptr &&c); + bool add_enum_fs( + const common::model::path &parent_path, std::unique_ptr &&c); + + bool add_package_ns(std::unique_ptr &&p); + bool add_package_fs(const common::model::path &parent_path, + std::unique_ptr &&p); + + bool add_concept_ns(std::unique_ptr &&p); + bool add_concept_fs( + const common::model::path &parent_path, std::unique_ptr &&p); + common::reference_vector classes_; common::reference_vector enums_; diff --git a/src/class_diagram/visitor/template_builder.cc b/src/class_diagram/visitor/template_builder.cc index 0e56f5e9..a7375d7f 100644 --- a/src/class_diagram/visitor/template_builder.cc +++ b/src/class_diagram/visitor/template_builder.cc @@ -23,14 +23,13 @@ namespace clanguml::class_diagram::visitor { -template_builder::template_builder(class_diagram::model::diagram &d, - const config::class_diagram &config, - common::visitor::ast_id_mapper &id_mapper, - clang::SourceManager &source_manager) - : diagram_{d} - , config_{config} - , id_mapper_{id_mapper} - , source_manager_{source_manager} +template_builder::template_builder( + clanguml::class_diagram::visitor::translation_unit_visitor &visitor) + : diagram_{visitor.diagram()} + , config_{visitor.config()} + , id_mapper_{visitor.id_mapper()} + , source_manager_{visitor.source_manager()} + , visitor_{visitor} { } @@ -268,6 +267,8 @@ std::unique_ptr template_builder::build(const clang::NamedDecl *cls, template_instantiation.set_id( common::to_id(template_instantiation_ptr->full_name(false))); + visitor_.set_source_location(*template_decl, *template_instantiation_ptr); + return template_instantiation_ptr; } @@ -1043,7 +1044,7 @@ template_builder::try_as_template_specialization_type( } if (diagram().should_include(nested_template_instantiation_full_name)) { - diagram().add_class(std::move(nested_template_instantiation)); + visitor_.add_class(std::move(nested_template_instantiation)); } return argument; @@ -1155,7 +1156,7 @@ std::optional template_builder::try_as_record_type( parent.value()->add_relationship( {relationship_t::kDependency, tag_argument->id()}); - diagram().add_class(std::move(tag_argument)); + visitor_.add_class(std::move(tag_argument)); } } } diff --git a/src/class_diagram/visitor/template_builder.h b/src/class_diagram/visitor/template_builder.h index 428d4b68..177f3a93 100644 --- a/src/class_diagram/visitor/template_builder.h +++ b/src/class_diagram/visitor/template_builder.h @@ -34,12 +34,12 @@ using found_relationships_t = std::vector>; +class translation_unit_visitor; + class template_builder { public: - template_builder(class_diagram::model::diagram &d, - const config::class_diagram &config, - common::visitor::ast_id_mapper &id_mapper, - clang::SourceManager &source_manager); + template_builder( + clanguml::class_diagram::visitor::translation_unit_visitor &visitor); class_diagram::model::diagram &diagram(); @@ -192,6 +192,8 @@ private: common::visitor::ast_id_mapper &id_mapper_; clang::SourceManager &source_manager_; + + clanguml::class_diagram::visitor::translation_unit_visitor &visitor_; }; } // namespace clanguml::class_diagram::visitor \ No newline at end of file diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index df42aae3..39d66b85 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -32,7 +32,7 @@ translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm, : common::visitor::translation_unit_visitor{sm, config} , diagram_{diagram} , config_{config} - , template_builder_{diagram_, config_, id_mapper_, sm} + , template_builder_{*this} { } @@ -84,7 +84,7 @@ bool translation_unit_visitor::VisitNamespaceDecl(clang::NamespaceDecl *ns) } if (!p->skip()) { - diagram().add_package(std::move(p)); + diagram().add_package(package_path, std::move(p)); } } @@ -170,7 +170,7 @@ bool translation_unit_visitor::VisitEnumDecl(clang::EnumDecl *enm) } if (diagram().should_include(qualified_name)) - diagram().add_enum(std::move(e_ptr)); + add_enum(std::move(e_ptr)); return true; } @@ -225,14 +225,14 @@ bool translation_unit_visitor::VisitClassTemplateSpecializationDecl( {relationship_t::kInstantiation, maybe_id.value()}); } - if (diagram_.should_include(template_specialization)) { + if (diagram().should_include(template_specialization)) { const auto full_name = template_specialization.full_name(false); const auto id = template_specialization.id(); LOG_DBG("Adding class template specialization {} with id {}", full_name, id); - diagram_.add_class(std::move(template_specialization_ptr)); + add_class(std::move(template_specialization_ptr)); } return true; @@ -265,13 +265,13 @@ bool translation_unit_visitor::VisitTypeAliasTemplateDecl( if (!template_specialization_ptr) return true; - if (diagram_.should_include(*template_specialization_ptr)) { + if (diagram().should_include(*template_specialization_ptr)) { const auto name = template_specialization_ptr->full_name(); const auto id = template_specialization_ptr->id(); LOG_DBG("Adding class {} with id {}", name, id); - diagram_.add_class(std::move(template_specialization_ptr)); + add_class(std::move(template_specialization_ptr)); } return true; @@ -327,11 +327,11 @@ bool translation_unit_visitor::VisitClassTemplateDecl( process_class_declaration(*cls->getTemplatedDecl(), *c_ptr); forward_declarations_.erase(id); - if (diagram_.should_include(*c_ptr)) { + if (diagram().should_include(*c_ptr)) { const auto name = c_ptr->full_name(); LOG_DBG("Adding class template {} with id {}", name, id); - diagram_.add_class(std::move(c_ptr)); + add_class(std::move(c_ptr)); } return true; @@ -380,11 +380,11 @@ bool translation_unit_visitor::VisitRecordDecl(clang::RecordDecl *rec) } forward_declarations_.erase(id); - if (diagram_.should_include(record_model)) { + if (diagram().should_include(record_model)) { LOG_DBG("Adding struct/union {} with id {}", record_model.full_name(false), record_model.id()); - diagram_.add_class(std::move(record_ptr)); + add_class(std::move(record_ptr)); } else { LOG_DBG("Skipping struct/union {} with id {}", record_model.full_name(), @@ -436,11 +436,11 @@ bool translation_unit_visitor::TraverseConceptDecl(clang::ConceptDecl *cpt) *concept_model, cpt->getConstraintExpr()); } - if (diagram_.should_include(*concept_model)) { + if (diagram().should_include(*concept_model)) { LOG_DBG("Adding concept {} with id {}", concept_model->full_name(false), concept_model->id()); - diagram_.add_concept(std::move(concept_model)); + add_concept(std::move(concept_model)); } else { LOG_DBG("Skipping concept {} with id {}", concept_model->full_name(), @@ -2105,23 +2105,46 @@ void translation_unit_visitor::add_class(std::unique_ptr &&c) common::model::path p{file, common::model::path_type::kFilesystem}; p.pop_back(); - // Make sure all parent directories are already packages in the model - for (auto it = p.begin(); it != p.end(); it++) { - auto pkg = std::make_unique( - config().using_namespace()); - pkg->set_name(*it); - auto ns = common::model::path(p.begin(), it); - // ns.pop_back(); - pkg->set_namespace(ns); - pkg->set_id(common::to_id(pkg->full_name(false))); - - diagram().add_package_fs(std::move(pkg)); - } - - diagram().add_class_fs(p, std::move(c)); + diagram().add_class(p, std::move(c)); } else { - diagram().add_class(std::move(c)); + diagram().add_class(c->path(), std::move(c)); + } +} + +void translation_unit_visitor::add_enum(std::unique_ptr &&e) +{ + if ((config().generate_packages() && + config().package_type() == config::package_type_t::kDirectory)) { + assert(!e->file().empty()); + + const auto file = config().make_path_relative(e->file()); + + common::model::path p{file, common::model::path_type::kFilesystem}; + p.pop_back(); + + diagram().add_enum(p, std::move(e)); + } + else { + diagram().add_enum(e->path(), std::move(e)); + } +} + +void translation_unit_visitor::add_concept(std::unique_ptr &&c) +{ + if ((config().generate_packages() && + config().package_type() == config::package_type_t::kDirectory)) { + assert(!c->file().empty()); + + const auto file = config().make_path_relative(c->file()); + + common::model::path p{file, common::model::path_type::kFilesystem}; + p.pop_back(); + + diagram().add_concept(p, std::move(c)); + } + else { + diagram().add_concept(c->path(), std::move(c)); } } diff --git a/src/class_diagram/visitor/translation_unit_visitor.h b/src/class_diagram/visitor/translation_unit_visitor.h index 3458db52..943943e7 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.h +++ b/src/class_diagram/visitor/translation_unit_visitor.h @@ -43,6 +43,7 @@ using clanguml::class_diagram::model::class_; using clanguml::class_diagram::model::class_member; using clanguml::class_diagram::model::class_method; using clanguml::class_diagram::model::class_parent; +using clanguml::class_diagram::model::concept_; using clanguml::class_diagram::model::diagram; using clanguml::class_diagram::model::enum_; using clanguml::class_diagram::model::method_parameter; @@ -118,9 +119,13 @@ public: */ void finalize(); -private: - void add_class(std::unique_ptr &&c); + common::visitor::ast_id_mapper &id_mapper() const { return id_mapper_; } + void add_class(std::unique_ptr &&c); + void add_enum(std::unique_ptr &&e); + void add_concept(std::unique_ptr &&c); + +private: bool should_include(const clang::NamedDecl *decl); std::unique_ptr @@ -220,8 +225,6 @@ private: bool has_processed_template_class(const std::string &qualified_name) const; - common::visitor::ast_id_mapper &id_mapper() const { return id_mapper_; } - template_builder &tbuilder() { return template_builder_; } // Reference to the output diagram model diff --git a/src/common/visitor/translation_unit_visitor.h b/src/common/visitor/translation_unit_visitor.h index 5b0d920f..4faaf2f0 100644 --- a/src/common/visitor/translation_unit_visitor.h +++ b/src/common/visitor/translation_unit_visitor.h @@ -62,7 +62,6 @@ public: */ clang::SourceManager &source_manager() const; -protected: /** * @brief Set source location in diagram element * @@ -93,6 +92,7 @@ protected: void set_source_location(const clang::SourceLocation &location, clanguml::common::model::source_location &element); +protected: /** * @brief Set source location in diagram element *