diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 4dd80dcd..92595dc7 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -17,6 +17,7 @@ */ #include "translation_unit_visitor.h" +#include "common/clang_utils.h" #include "cx/util.h" #include @@ -74,7 +75,7 @@ std::optional get_enclosing_namespace( return {}; } - return namespace_{namespace_declaration->getQualifiedNameAsString()}; + return namespace_{common::get_qualified_name(*namespace_declaration)}; } } @@ -143,10 +144,12 @@ translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm, bool translation_unit_visitor::VisitNamespaceDecl(clang::NamespaceDecl *ns) { + assert(ns != nullptr); + if (ns->isAnonymousNamespace() || ns->isInline()) return true; - auto package_path = namespace_{ns->getQualifiedNameAsString()}; + auto package_path = namespace_{common::get_qualified_name(*ns)}; auto package_parent = package_path; std::string name; @@ -163,9 +166,9 @@ bool translation_unit_visitor::VisitNamespaceDecl(clang::NamespaceDecl *ns) p->set_name(name); p->set_namespace(package_parent); - p->set_id(ns->getID()); + p->set_id(common::to_id(*ns)); - if (diagram().should_include(*p)) { + if (diagram().should_include(*p) && !diagram().get(p->id())) { process_comment(*ns, *p); set_source_location(*ns, *p); @@ -188,6 +191,8 @@ bool translation_unit_visitor::VisitNamespaceDecl(clang::NamespaceDecl *ns) bool translation_unit_visitor::VisitEnumDecl(clang::EnumDecl *enm) { + assert(enm != nullptr); + // Anonymous enum values should be rendered as class fields // with type enum if (enm->getNameAsString().empty()) @@ -196,7 +201,7 @@ bool translation_unit_visitor::VisitEnumDecl(clang::EnumDecl *enm) auto e_ptr = std::make_unique(config_.using_namespace()); auto &e = *e_ptr; - std::string qualified_name = enm->getQualifiedNameAsString(); + std::string qualified_name = common::get_qualified_name(*enm); namespace_ ns{qualified_name}; ns.pop_back(); e.set_name(enm->getNameAsString()); @@ -401,19 +406,19 @@ bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls) std::unique_ptr translation_unit_visitor::create_class_declaration( clang::CXXRecordDecl *cls) { + assert(cls != nullptr); + auto c_ptr{std::make_unique(config_.using_namespace())}; auto &c = *c_ptr; // TODO: refactor to method get_qualified_name() - auto qualified_name = cls->getQualifiedNameAsString(); - util::replace_all(qualified_name, "(anonymous namespace)", ""); - util::replace_all(qualified_name, "::::", "::"); + auto qualified_name = common::get_qualified_name(*cls); namespace_ ns{qualified_name}; ns.pop_back(); c.set_name(cls->getNameAsString()); c.set_namespace(ns); - c.set_id(cls->getID()); + c.set_id(common::to_id(*cls)); c.is_struct(cls->isStruct()); @@ -449,7 +454,7 @@ bool translation_unit_visitor::process_template_parameters( const clang::ClassTemplateDecl &template_declaration, class_ &c) { LOG_DBG("Processing class {} template parameters...", - template_declaration.getQualifiedNameAsString()); + common::get_qualified_name(template_declaration)); if (template_declaration.getTemplateParameters() == nullptr) return false; @@ -520,8 +525,8 @@ void translation_unit_visitor::process_record_containment( element.set_namespace(namespace_declaration.value()); } - const auto id = - static_cast(record.getParent())->getID(); + const auto id = common::to_id( + *static_cast(record.getParent())); element.add_relationship({relationship_t::kContainment, id}); } @@ -541,11 +546,8 @@ void translation_unit_visitor::process_class_bases( base.getType()->getAs()->getDecl()->getID()); else if (base.getType()->getAs() != nullptr) { - cp.set_id(base.getType() - ->getAs() - ->getTemplateName() - .getAsTemplateDecl() - ->getID()); + cp.set_id(common::to_id( + *base.getType()->getAs())); } else // This could be a template parameter - we don't want it here diff --git a/src/common/clang_utils.cc b/src/common/clang_utils.cc new file mode 100644 index 00000000..208909a3 --- /dev/null +++ b/src/common/clang_utils.cc @@ -0,0 +1,34 @@ +/** + * src/common/visitor/clang_utils.cc + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "clang_utils.h" + +namespace clanguml::common { + +template <> id_t to_id(const clang::NamespaceDecl &declaration) +{ + return std::hash{}(get_qualified_name(declaration)) >> 3; +} + +template <> id_t to_id(const clang::EnumType &t) { return to_id(*t.getDecl()); } + +template <> id_t to_id(const clang::TemplateSpecializationType &t) +{ + return t.getTemplateName().getAsTemplateDecl()->getID(); +} +} diff --git a/src/common/clang_utils.h b/src/common/clang_utils.h new file mode 100644 index 00000000..1adcdf8a --- /dev/null +++ b/src/common/clang_utils.h @@ -0,0 +1,52 @@ +/** + * src/common/clang_utils.h + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "cx/util.h" +#include "types.h" + +#include + +#include + +namespace clang { +class NamespaceDecl; +} + +namespace clanguml::common { + +template std::string get_qualified_name(const T &declaration) +{ + auto qualified_name = declaration.getQualifiedNameAsString(); + util::replace_all(qualified_name, "(anonymous namespace)", ""); + util::replace_all(qualified_name, "::::", "::"); + return qualified_name; +} + +template id_t to_id(const T &declaration) +{ + return declaration.getID(); +} + +template <> id_t to_id(const clang::NamespaceDecl &declaration); + +template <> id_t to_id(const clang::EnumType &type); + +template <> id_t to_id(const clang::TemplateSpecializationType &type); + +} diff --git a/src/package_diagram/visitor/translation_unit_visitor.cc b/src/package_diagram/visitor/translation_unit_visitor.cc index ccb35197..4ac33684 100644 --- a/src/package_diagram/visitor/translation_unit_visitor.cc +++ b/src/package_diagram/visitor/translation_unit_visitor.cc @@ -18,6 +18,7 @@ #include "translation_unit_visitor.h" +#include "common/clang_utils.h" #include "common/model/namespace.h" #include "cx/util.h" @@ -35,15 +36,6 @@ 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) @@ -55,12 +47,12 @@ translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm, bool translation_unit_visitor::VisitNamespaceDecl(clang::NamespaceDecl *ns) { + assert(ns != nullptr); + if (ns->isAnonymousNamespace() || ns->isInline()) return true; - auto qualified_name = ns->getQualifiedNameAsString(); - util::replace_all(qualified_name, "(anonymous namespace)", ""); - util::replace_all(qualified_name, "::::", "::"); + auto qualified_name = common::get_qualified_name(*ns); LOG_DBG("Visiting namespace declaration: {}", qualified_name); @@ -81,7 +73,7 @@ bool translation_unit_visitor::VisitNamespaceDecl(clang::NamespaceDecl *ns) p->set_name(name); p->set_namespace(package_parent); - p->set_id(to_id(ns)); + p->set_id(common::to_id(*ns)); assert(p->id() > 0); @@ -160,7 +152,7 @@ void translation_unit_visitor::add_relationships( const auto *namespace_context = cls->getEnclosingNamespaceContext(); if (namespace_context != nullptr && namespace_context->isNamespace()) { current_package_id = - to_id(llvm::cast(namespace_context)); + common::to_id(*llvm::cast(namespace_context)); } assert(current_package_id != 0); @@ -333,8 +325,7 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type, } else if (type->isEnumeralType()) { relationships.emplace_back( - type->getAs()->getDecl()->getID(), - relationship_hint); + common::to_id(*type->getAs()), relationship_hint); } else if (auto *template_specialization_type = type->getAs()) { @@ -398,10 +389,11 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type, 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)); + if (namespace_declaration != nullptr && + diagram().should_include( + common::get_qualified_name(*namespace_declaration))) { + const auto target_id = common::to_id( + *clang::cast(namespace_context)); relationships.emplace_back(target_id, relationship_hint); result = true; }