From 7274809561914d2c33e9f82b9bf6a3762ffdeadd Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Sun, 31 Jul 2022 16:14:36 +0200 Subject: [PATCH] Fixed package diagram generation --- src/class_diagram/model/diagram.cc | 48 ++- src/class_diagram/model/diagram.h | 26 +- .../visitor/translation_unit_visitor.cc | 3 +- .../visitor/translation_unit_visitor.h | 3 - src/common/model/diagram.h | 8 +- src/common/model/diagram_filter.cc | 4 +- src/common/types.h | 4 +- src/include_diagram/model/diagram.cc | 8 +- src/include_diagram/model/diagram.h | 7 +- .../plantuml/package_diagram_generator.cc | 2 +- src/package_diagram/model/diagram.cc | 47 ++- src/package_diagram/model/diagram.h | 22 +- .../visitor/translation_unit_visitor.cc | 368 ++++++++++++++++++ .../visitor/translation_unit_visitor.h | 70 ++++ src/sequence_diagram/model/diagram.cc | 6 + src/sequence_diagram/model/diagram.h | 3 + tests/t30001/test_case.h | 2 +- tests/t30002/t30002.cc | 13 +- tests/t30002/test_case.h | 6 +- tests/t30003/test_case.h | 2 +- tests/t30004/test_case.h | 2 +- tests/t30005/test_case.h | 2 +- tests/t30006/test_case.h | 2 +- tests/t30007/test_case.h | 2 +- tests/t30008/test_case.h | 2 +- tests/test_cases.cc | 16 +- 26 files changed, 600 insertions(+), 78 deletions(-) diff --git a/src/class_diagram/model/diagram.cc b/src/class_diagram/model/diagram.cc index 50f9ab19..e3522b62 100644 --- a/src/class_diagram/model/diagram.cc +++ b/src/class_diagram/model/diagram.cc @@ -25,13 +25,13 @@ namespace clanguml::class_diagram::model { -const std::vector> & +const std::vector> & diagram::classes() const { return classes_; } -const std::vector> &diagram::enums() const +const std::vector> &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> +std::optional> diagram::get(const std::string &full_name) const { - // type_safe::optional_ref - // res; std::optional< - std::reference_wrapper> + std::reference_wrapper> res; res = get_class(full_name); @@ -61,6 +58,23 @@ diagram::get(const std::string &full_name) const return res; } +std::optional> +diagram::get(const clanguml::common::model::diagram_element::id_t id) const +{ + std::optional< + std::reference_wrapper> + 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> diagram::get_class( +std::optional> diagram::get_class( const std::string &name) const { for (const auto &c : classes_) { @@ -87,7 +101,7 @@ std::optional> diagram::get_class( return {}; } -std::optional> diagram::get_class( +std::optional> diagram::get_class( clanguml::common::model::diagram_element::id_t id) const { for (const auto &c : classes_) { @@ -99,7 +113,7 @@ std::optional> diagram::get_class( return {}; } -std::optional> diagram::get_enum( +std::optional> diagram::get_enum( const std::string &name) const { for (const auto &e : enums_) { @@ -111,6 +125,18 @@ std::optional> diagram::get_enum( return {}; } +std::optional> 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 &&ta) { LOG_DBG( @@ -149,7 +175,7 @@ bool diagram::add_class(std::unique_ptr &&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); diff --git a/src/class_diagram/model/diagram.h b/src/class_diagram/model/diagram.h index 2b322a86..26cfb29c 100644 --- a/src/class_diagram/model/diagram.h +++ b/src/class_diagram/model/diagram.h @@ -46,26 +46,33 @@ public: common::model::diagram_t type() const override; std::optional< - std::reference_wrapper> + std::reference_wrapper> get(const std::string &full_name) const override; - const std::vector> &classes() const; + std::optional< + std::reference_wrapper> + get(const clanguml::common::model::diagram_element::id_t id) const override; - const std::vector> &enums() const; + const std::vector> &classes() const; + + const std::vector> &enums() const; bool has_class(const class_ &c) const; bool has_enum(const enum_ &e) const; - std::optional> get_class( + std::optional> get_class( const std::string &name) const; - std::optional> get_class( + std::optional> get_class( clanguml::common::model::diagram_element::id_t id) const; - std::optional> get_enum( + std::optional> get_enum( const std::string &name) const; + std::optional> get_enum( + clanguml::common::model::diagram_element::id_t id) const; + void add_type_alias(std::unique_ptr &&ta); bool add_class(std::unique_ptr &&c); @@ -85,11 +92,10 @@ public: const clanguml::common::model::diagram_element::id_t id) const override; private: - std::vector> classes_; - std::vector> enums_; + std::vector> classes_; + + std::vector> enums_; - // std::vector> classes_; - // std::vector> enums_; std::map> type_aliases_; }; } diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index c0cc2db0..4dd80dcd 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -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; diff --git a/src/class_diagram/visitor/translation_unit_visitor.h b/src/class_diagram/visitor/translation_unit_visitor.h index f312faf1..d7bea1f5 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.h +++ b/src/class_diagram/visitor/translation_unit_visitor.h @@ -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: diff --git a/src/common/model/diagram.h b/src/common/model/diagram.h index 101872bb..0d58506a 100644 --- a/src/common/model/diagram.h +++ b/src/common/model/diagram.h @@ -34,14 +34,19 @@ class relationship; class diagram { public: diagram(); + virtual ~diagram(); virtual diagram_t type() const = 0; virtual std::optional< - std::reference_wrapper> + std::reference_wrapper> get(const std::string &full_name) const = 0; + virtual std::optional< + std::reference_wrapper> + 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; diff --git a/src/common/model/diagram_filter.cc b/src/common/model/diagram_filter.cc index 1dec64dd..1fe1f568 100644 --- a/src/common/model/diagram_filter.cc +++ b/src/common/model/diagram_filter.cc @@ -63,14 +63,14 @@ const clanguml::common::optional_ref get( } template <> -const clanguml::common::optional_ref get( +const clanguml::common::optional_ref get( const package_diagram::model::diagram &d, const std::string &full_name) { return d.get_package(full_name); } template <> -const clanguml::common::optional_ref get( +const clanguml::common::optional_ref get( const include_diagram::model::diagram &d, const std::string &full_name) { return d.get_file(full_name); diff --git a/src/common/types.h b/src/common/types.h index 8d3329e6..ad6ef5ae 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -27,10 +27,10 @@ namespace clanguml::common { using id_t = int64_t; template -using optional_ref = std::optional>; +using optional_ref = std::optional>; template -using reference_vector = std::vector>; +using reference_vector = std::vector>; template using reference_set = std::unordered_set>; diff --git a/src/include_diagram/model/diagram.cc b/src/include_diagram/model/diagram.cc index d217fd89..163e330f 100644 --- a/src/include_diagram/model/diagram.cc +++ b/src/include_diagram/model/diagram.cc @@ -34,6 +34,12 @@ common::optional_ref diagram::get( return get_file(full_name); } +common::optional_ref diagram::get( + const common::model::diagram_element::id_t /*id*/) const +{ + return {}; +} + void diagram::add_file(std::unique_ptr &&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::reference_vector & diagram::files() const { return files_; diff --git a/src/include_diagram/model/diagram.h b/src/include_diagram/model/diagram.h index b494f5b7..473705ee 100644 --- a/src/include_diagram/model/diagram.h +++ b/src/include_diagram/model/diagram.h @@ -44,6 +44,9 @@ public: common::optional_ref get( const std::string &full_name) const override; + common::optional_ref get( + const common::model::diagram_element::id_t id) const override; + void add_file(std::unique_ptr &&f); common::optional_ref get_file( @@ -51,11 +54,11 @@ public: std::string to_alias(const std::string &full_name) const; - const common::reference_vector & + const common::reference_vector & files() const; private: - common::reference_vector files_; + common::reference_vector files_; }; } diff --git a/src/package_diagram/generators/plantuml/package_diagram_generator.cc b/src/package_diagram/generators/plantuml/package_diagram_generator.cc index f92638f2..788a3c76 100644 --- a/src/package_diagram/generators/plantuml/package_diagram_generator.cc +++ b/src/package_diagram/generators/plantuml/package_diagram_generator.cc @@ -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(); } diff --git a/src/package_diagram/model/diagram.cc b/src/package_diagram/model/diagram.cc index 18e8d604..96803b9b 100644 --- a/src/package_diagram/model/diagram.cc +++ b/src/package_diagram/model/diagram.cc @@ -28,8 +28,7 @@ common::model::diagram_t diagram::type() const return common::model::diagram_t::kPackage; } -const std::vector< - std::reference_wrapper> & +const std::vector> & diagram::packages() const { return packages_; @@ -46,7 +45,7 @@ void diagram::add_package(std::unique_ptr &&p) add_element(ns, std::move(p)); } -std::optional> +std::optional> 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> +std::optional> +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> 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> +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(path); - - if (!package) - throw error::uml_alias_missing( - fmt::format("Missing alias for '{}'", path.to_string())); - - return package.value().alias(); + return {}; } } diff --git a/src/package_diagram/model/diagram.h b/src/package_diagram/model/diagram.h index 2d39dac5..e7209ea0 100644 --- a/src/package_diagram/model/diagram.h +++ b/src/package_diagram/model/diagram.h @@ -39,24 +39,30 @@ public: common::model::diagram_t type() const override; - const std::vector< - std::reference_wrapper> & - packages() const; + const std::vector> + &packages() const; std::optional< - std::reference_wrapper> + std::reference_wrapper> get(const std::string &full_name) const override; + std::optional< + std::reference_wrapper> + get(const clanguml::common::model::diagram_element::id_t id) const override; + void add_package(std::unique_ptr &&p); - std::optional< - std::reference_wrapper> + std::optional> get_package(const std::string &name) const; - std::string to_alias(const std::string &full_name) const; + std::optional> 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::vector> packages_; }; } diff --git a/src/package_diagram/visitor/translation_unit_visitor.cc b/src/package_diagram/visitor/translation_unit_visitor.cc index 9ae8f65c..ccb35197 100644 --- a/src/package_diagram/visitor/translation_unit_visitor.cc +++ b/src/package_diagram/visitor/translation_unit_visitor.cc @@ -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{}(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(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(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(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(&cls)->decls()) { + auto const *method_template = + llvm::dyn_cast_or_null(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(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()->getDecl()->getID(), + relationship_hint); + } + else if (auto *template_specialization_type = + type->getAs()) { + if (template_specialization_type != nullptr) { + if (template_specialization_type->isTypeAlias()) + template_specialization_type = + template_specialization_type->getAliasedType() + ->getAs(); + } + + 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()) { + for (const auto ¶m_type : + template_argument.getAsType() + ->getAs() + ->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(namespace_context); + + if (diagram().should_include( + namespace_declaration->getQualifiedNameAsString())) { + const auto target_id = + to_id(clang::cast(namespace_context)); + relationships.emplace_back(target_id, relationship_hint); + result = true; + } + } + } + + return result; +} + } diff --git a/src/package_diagram/visitor/translation_unit_visitor.h b/src/package_diagram/visitor/translation_unit_visitor.h index 3a18f70e..c4df34ae 100644 --- a/src/package_diagram/visitor/translation_unit_visitor.h +++ b/src/package_diagram/visitor/translation_unit_visitor.h @@ -33,6 +33,10 @@ namespace clanguml::package_diagram::visitor { +using found_relationships_t = + std::vector>; + class translation_unit_visitor : public clang::RecursiveASTVisitor { 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 + 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 diff --git a/src/sequence_diagram/model/diagram.cc b/src/sequence_diagram/model/diagram.cc index 26a69a7b..89c75692 100644 --- a/src/sequence_diagram/model/diagram.cc +++ b/src/sequence_diagram/model/diagram.cc @@ -34,6 +34,12 @@ common::optional_ref diagram::get( return {}; } +common::optional_ref 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; diff --git a/src/sequence_diagram/model/diagram.h b/src/sequence_diagram/model/diagram.h index 96f297d7..23c00b96 100644 --- a/src/sequence_diagram/model/diagram.h +++ b/src/sequence_diagram/model/diagram.h @@ -40,6 +40,9 @@ public: common::optional_ref get( const std::string &full_name) const override; + common::optional_ref get( + const common::model::diagram_element::id_t id) const override; + std::string to_alias(const std::string &full_name) const; bool started{false}; diff --git a/tests/t30001/test_case.h b/tests/t30001/test_case.h index fad80d6c..be84d0fd 100644 --- a/tests/t30001/test_case.h +++ b/tests/t30001/test_case.h @@ -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"); diff --git a/tests/t30002/t30002.cc b/tests/t30002/t30002.cc index 33952535..5766e869 100644 --- a/tests/t30002/t30002.cc +++ b/tests/t30002/t30002.cc @@ -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 cc_; std::map> *cd_; std::array co_; + static A::AA::A16::CP* cp_; CBA() = default; CBA(A::AA::A14::CN *cn) { } + friend A::AA::A17::CR; + template CBA(std::tuple &items) { } void ce(const std::vector /*ce_*/) { } @@ -87,7 +98,7 @@ public: std::shared_ptr cg() { return {}; } template - void ch(std::map> & /*ch_*/) + void ch(std::map> & ch_) { } diff --git a/tests/t30002/test_case.h b/tests/t30002/test_case.h index 765dc8a8..0eb66a3e 100644 --- a/tests/t30002/test_case.h +++ b/tests/t30002/test_case.h @@ -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); diff --git a/tests/t30003/test_case.h b/tests/t30003/test_case.h index faa29614..7a7d9321 100644 --- a/tests/t30003/test_case.h +++ b/tests/t30003/test_case.h @@ -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"); diff --git a/tests/t30004/test_case.h b/tests/t30004/test_case.h index e850084b..ac6700b5 100644 --- a/tests/t30004/test_case.h +++ b/tests/t30004/test_case.h @@ -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"); diff --git a/tests/t30005/test_case.h b/tests/t30005/test_case.h index 30c181f3..57fb3d6c 100644 --- a/tests/t30005/test_case.h +++ b/tests/t30005/test_case.h @@ -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"); diff --git a/tests/t30006/test_case.h b/tests/t30006/test_case.h index 4991f12a..944da322 100644 --- a/tests/t30006/test_case.h +++ b/tests/t30006/test_case.h @@ -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"); diff --git a/tests/t30007/test_case.h b/tests/t30007/test_case.h index 03850092..3212d067 100644 --- a/tests/t30007/test_case.h +++ b/tests/t30007/test_case.h @@ -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"); diff --git a/tests/t30008/test_case.h b/tests/t30008/test_case.h index caace744..9d62f64c 100644 --- a/tests/t30008/test_case.h +++ b/tests/t30008/test_case.h @@ -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"); diff --git a/tests/test_cases.cc b/tests/test_cases.cc index d7f330d0..7f98c0c9 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -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