22#include <clang/AST/ExprConcepts.h>
23#include <clang/Basic/FileManager.h>
24#include <clang/Lex/Preprocessor.h>
25#include <spdlog/spdlog.h>
33 , template_builder_{
diagram, config, *this}
38 const clang::NamedDecl *decl)
const
40 auto cls = std::make_unique<class_>(
config().using_namespace());
47 assert(ns !=
nullptr);
52 if (ns->isAnonymousNamespace() || ns->isInline())
55 LOG_DBG(
"= Visiting namespace declaration {} at {}",
56 ns->getQualifiedNameAsString(),
60 auto package_parent = package_path;
63 if (!package_path.is_empty())
64 name = package_path.name();
66 if (!package_parent.is_empty())
67 package_parent.pop_back();
69 const auto usn =
config().using_namespace();
71 auto p = std::make_unique<common::model::package>(usn);
72 package_path = package_path.relative_to(usn);
75 p->set_namespace(package_parent);
84 p->set_style(p->style_spec());
86 for (
const auto *attr : ns->attrs()) {
87 if (attr->getKind() == clang::attr::Kind::Deprecated) {
88 p->set_deprecated(
true);
94 diagram().add(package_path, std::move(p));
103 assert(enm !=
nullptr);
107 if (enm->getNameAsString().empty())
113 LOG_DBG(
"= Visiting enum declaration {} at {}",
114 enm->getQualifiedNameAsString(),
117 auto e_ptr = std::make_unique<enum_>(
config().using_namespace());
124 const auto *parent = enm->getParent();
127 std::optional<eid_t> parent_id_opt;
129 if (parent !=
nullptr) {
130 const auto *parent_record_decl =
131 clang::dyn_cast<clang::RecordDecl>(parent);
133 if (parent_record_decl !=
nullptr) {
134 eid_t local_id{parent_record_decl->getID()};
141 if (!parent_id_opt) {
142 if (parent_record_decl->getDescribedTemplate() !=
nullptr) {
144 parent_record_decl->getDescribedTemplate()->getID();
151 if (parent_id_opt &&
diagram().find<class_>(*parent_id_opt)) {
155 e.set_name(parent_class.value().name() +
"##" + enm->getNameAsString());
157 e.add_relationship({relationship_t::kContainment, *parent_id_opt});
175 e.set_style(e.style_spec());
177 for (
const auto &ev : enm->enumerators()) {
178 e.constants().push_back(ev->getNameAsString());
187 clang::ClassTemplateSpecializationDecl *cls)
192 LOG_DBG(
"= Visiting template specialization declaration {} at {} "
193 "(described class id {})",
194 cls->getQualifiedNameAsString(),
196 cls->getSpecializedTemplate()
197 ? cls->getSpecializedTemplate()->getTemplatedDecl()->getID()
201 if (cls->isLocalClass() !=
nullptr)
206 if (!template_specialization_ptr)
209 auto &template_specialization = *template_specialization_ptr;
211 if (cls->hasDefinition()) {
219 if (!template_specialization.template_specialization_found()) {
222 const eid_t ast_id{cls->getSpecializedTemplate()->getID()};
224 if (maybe_id.has_value())
225 template_specialization.add_relationship(
226 {relationship_t::kInstantiation, maybe_id.value()});
230 const auto full_name = template_specialization.full_name(
false);
231 const auto id = template_specialization.id();
233 LOG_DBG(
"Adding class template specialization {} with id {}", full_name,
236 add_class(std::move(template_specialization_ptr));
243 clang::TypeAliasTemplateDecl *cls)
248 LOG_DBG(
"= Visiting template type alias declaration {} at {}",
249 cls->getQualifiedNameAsString(),
252 const auto *template_type_specialization_ptr =
253 cls->getTemplatedDecl()
254 ->getUnderlyingType()
255 ->getAs<clang::TemplateSpecializationType>();
257 if (template_type_specialization_ptr ==
nullptr)
260 auto template_specialization_ptr =
261 std::make_unique<class_>(
config().using_namespace());
263 *template_specialization_ptr, cls, *template_type_specialization_ptr);
266 const auto name = template_specialization_ptr->full_name();
267 const auto id = template_specialization_ptr->id();
269 LOG_DBG(
"Adding class {} with id {}", name,
id);
274 add_class(std::move(template_specialization_ptr));
281 clang::ClassTemplateDecl *cls)
286 LOG_DBG(
"= Visiting class template declaration {} at {}",
287 cls->getQualifiedNameAsString(),
301 const auto cls_full_name = c_ptr->full_name(
false);
305 c_ptr->is_template(
true);
309 constexpr auto kMaxConstraintCount = 24U;
310 llvm::SmallVector<const clang::Expr *, kMaxConstraintCount> constraints{};
311 if (cls->hasAssociatedConstraints()) {
312 cls->getAssociatedConstraints(constraints);
315 for (
const auto *expr : constraints) {
319 if (!cls->getTemplatedDecl()->isCompleteDefinition()) {
327 const auto name = c_ptr->full_name();
328 LOG_DBG(
"Adding class template {} with id {}", name,
id);
338 if (clang::dyn_cast_or_null<clang::CXXRecordDecl>(rec) !=
nullptr)
346 LOG_DBG(
"= Visiting record declaration {} at {}",
347 rec->getQualifiedNameAsString(),
355 const auto rec_id = record_ptr->id();
359 auto &record_model =
diagram().find<
class_>(rec_id).has_value()
363 if (rec->isCompleteDefinition() && !record_model.complete()) {
365 record_model.complete(
true);
368 auto id = record_model.id();
369 if (!rec->isCompleteDefinition()) {
376 LOG_DBG(
"Adding struct/union {} with id {}",
377 record_model.full_name(
false), record_model.id());
382 LOG_DBG(
"Skipping struct/union {} with id {}", record_model.full_name(),
394 LOG_DBG(
"= Visiting concept (isType: {}) declaration {} at {}",
395 cpt->isTypeConcept(), cpt->getQualifiedNameAsString(),
403 const auto concept_id = concept_model->id();
409 constexpr auto kMaxConstraintCount = 24U;
410 llvm::SmallVector<const clang::Expr *, kMaxConstraintCount> constraints{};
411 if (cpt->hasAssociatedConstraints()) {
412 cpt->getAssociatedConstraints(constraints);
415 for (
const auto *expr : constraints) {
419 if (cpt->getConstraintExpr() !=
nullptr) {
421 cpt, cpt->getConstraintExpr(), *concept_model);
424 *concept_model, cpt->getConstraintExpr());
428 LOG_DBG(
"Adding concept {} with id {}", concept_model->full_name(
false),
429 concept_model->id());
434 LOG_DBG(
"Skipping concept {} with id {}", concept_model->full_name(),
435 concept_model->id());
442 const clang::ConceptDecl *cpt,
const clang::Expr *expr,
445 if (
const auto *constraint = llvm::dyn_cast<clang::RequiresExpr>(expr);
450 LOG_DBG(
"== Processing constraint: '{}'", constraint_source);
452 for ([[maybe_unused]]
const auto *requirement :
453 constraint->getRequirements()) {
458 for (
const auto *decl : constraint->getBody()->decls()) {
459 if (
const auto *parm_var_decl =
460 llvm::dyn_cast<clang::ParmVarDecl>(decl);
462 parm_var_decl->getQualifiedNameAsString();
464 auto param_name = parm_var_decl->getNameAsString();
466 parm_var_decl->getType(), cpt->getASTContext());
468 LOG_DBG(
"=== Processing parameter variable declaration: {}, {}",
469 param_type, param_name);
472 {std::move(param_type), std::move(param_name)});
475 LOG_DBG(
"=== Processing some other concept declaration: {}",
481 for (
const auto *req : constraint->getRequirements()) {
482 if (req->getKind() == clang::concepts::Requirement::RK_Simple) {
483 const auto *simple_req =
484 llvm::dyn_cast<clang::concepts::ExprRequirement>(req);
486 if (simple_req !=
nullptr) {
488 simple_req->getExpr(), [&concept_model](
const auto *e) {
489 auto simple_expr = common::to_string(e);
491 LOG_DBG(
"=== Processing expression requirement: {}",
494 concept_model.add_statement(std::move(simple_expr));
498 else if (req->getKind() == clang::concepts::Requirement::RK_Type) {
500 llvm::dyn_cast<clang::concepts::TypeRequirement>(req),
501 [&concept_model, cpt](
const auto *t) {
503 t->getType()->getType(), cpt->getASTContext());
506 "=== Processing type requirement: {}", type_name);
511 else if (req->getKind() ==
512 clang::concepts::Requirement::RK_Nested) {
513 const auto *nested_req =
514 llvm::dyn_cast<clang::concepts::NestedRequirement>(req);
516 if (nested_req !=
nullptr) {
518 nested_req->getConstraintExpr(), [](
const auto *e) {
519 LOG_DBG(
"=== Processing nested requirement: {}",
520 common::to_string(e));
524 else if (req->getKind() ==
525 clang::concepts::Requirement::RK_Compound) {
526 const auto *compound_req =
527 llvm::dyn_cast<clang::concepts::ExprRequirement>(req);
529 if (compound_req !=
nullptr) {
530 const auto *compound_expr_ptr = compound_req->getExpr();
532 if (compound_expr_ptr !=
nullptr) {
536 auto req_return_type =
537 compound_req->getReturnTypeRequirement();
539 if (!req_return_type.isEmpty()) {
541 fmt::format(
"{{{}}} -> {}", compound_expr,
543 req_return_type.getTypeConstraint()));
545 else if (compound_req->hasNoexceptRequirement()) {
547 fmt::format(
"{{{}}} noexcept", compound_expr);
550 LOG_DBG(
"=== Processing compound requirement: {}",
559 else if (
const auto *binop = llvm::dyn_cast<clang::BinaryOperator>(expr);
564 else if (
const auto *unop = llvm::dyn_cast<clang::UnaryOperator>(expr);
575 found_relationships_t relationships;
577 common::if_dyn_cast<clang::UnresolvedLookupExpr>(expr, [&](
const auto *ul) {
578 for (
const auto ta : ul->template_arguments()) {
580 relationship_t::kConstraint);
584 common::if_dyn_cast<clang::ConceptSpecializationExpr>(
585 expr, [&](
const auto *cs) {
589 common::if_dyn_cast<clang::RequiresExpr>(expr, [&](
const auto *re) {
593 common::if_dyn_cast<clang::BinaryOperator>(expr, [&](
const auto *op) {
598 common::if_dyn_cast<clang::UnaryOperator>(expr, [&](
const auto *op) {
602 for (
const auto &[type_element_id, relationship_type] : relationships) {
603 if (type_element_id != c.
id() &&
604 (relationship_type != relationship_t::kNone)) {
615 const clang::ConceptSpecializationExpr *concept_specialization)
618 if (
const auto *cpt = concept_specialization->getNamedConcept();
621 const auto cpt_name = cpt->getNameAsString();
622 const eid_t ast_id{cpt->getID()};
627 const auto target_id = maybe_id.value();
629 std::vector<std::string> constrained_template_params;
631 size_t argument_index{};
633 for (
const auto ta : concept_specialization->getTemplateArguments()) {
634 if (ta.getKind() == clang::TemplateArgument::Type) {
638 cpt, constrained_template_params, argument_index,
641 else if (ta.getKind() == clang::TemplateArgument::Pack) {
642 if (!ta.getPackAsArray().empty() &&
643 ta.getPackAsArray().front().isPackExpansion()) {
644 const auto &pack_head =
645 ta.getPackAsArray().front().getAsType();
649 concept_specialization, cpt,
650 constrained_template_params, argument_index, type_name);
657 "=== Unsupported concept type parameter: {}", type_name);
662 if (!constrained_template_params.empty())
664 {relationship_t::kConstraint, target_id, access_t::kNone,
666 "{}", fmt::join(constrained_template_params,
","))});
675 LOG_DBG(
"= Visiting class declaration {} at {}",
676 cls->getQualifiedNameAsString(),
680 "== getQualifiedNameAsString() = {}", cls->getQualifiedNameAsString());
681 if (cls->getOwningModule() !=
nullptr)
683 "== getOwningModule()->Name = {}", cls->getOwningModule()->Name);
684 LOG_DBG(
"== getID() = {}", cls->getID());
685 LOG_DBG(
"== isTemplateDecl() = {}", cls->isTemplateDecl());
686 LOG_DBG(
"== isTemplated() = {}", cls->isTemplated());
687 LOG_DBG(
"== getParent()->isRecord()() = {}", cls->getParent()->isRecord());
689 if (
const auto *parent_record =
690 clang::dyn_cast<clang::RecordDecl>(cls->getParent());
691 parent_record !=
nullptr) {
692 LOG_DBG(
"== getParent()->getQualifiedNameAsString() = {}",
693 parent_record->getQualifiedNameAsString());
701 if (cls->isTemplated() && (cls->getDescribedTemplate() !=
nullptr)) {
704 const eid_t ast_id{cls->getDescribedTemplate()->getID()};
710 if (cls->isLocalClass() !=
nullptr)
718 const auto cls_id = c_ptr->id();
722 auto &class_model =
diagram().find<
class_>(cls_id).has_value()
726 if (cls->isCompleteDefinition() && !class_model.complete())
729 auto id = class_model.id();
730 if (!cls->isCompleteDefinition()) {
737 LOG_DBG(
"Adding class {} with id {}", class_model.full_name(
false),
743 LOG_DBG(
"Skipping class {} with id {}", class_model.full_name(),
750std::unique_ptr<clanguml::class_diagram::model::concept_>
753 assert(cpt !=
nullptr);
759 std::make_unique<model::concept_>(
config().using_namespace())};
760 auto &concept_model = *concept_ptr;
764 concept_model.set_name(cpt->getNameAsString());
765 concept_model.set_namespace(ns);
766 concept_model.set_id(
common::to_id(concept_model.full_name(
false)));
772 if (concept_model.skip())
775 concept_model.set_style(concept_model.style_spec());
781 clang::RecordDecl *rec)
783 assert(rec !=
nullptr);
788 auto record_ptr{std::make_unique<class_>(
config().using_namespace())};
789 auto &record = *record_ptr;
793 if (!record.is_nested()) {
794 auto record_name = rec->getQualifiedNameAsString();
796#if LLVM_VERSION_MAJOR < 16
797 if (record_name ==
"(anonymous)") {
799 [&record_name](
const clang::TypedefNameDecl *name) {
800 record_name = name->getNameAsString();
805 record.set_name(record_name);
813 const auto record_full_name = record_ptr->full_name(
false);
815 record.is_struct(rec->isStruct());
816 record.is_union(rec->isUnion());
821 record.set_style(record.style_spec());
827 clang::CXXRecordDecl *cls)
829 assert(cls !=
nullptr);
834 auto c_ptr{std::make_unique<class_>(
config().using_namespace())};
841 if (!c.is_nested()) {
847 c.is_struct(cls->isStruct());
856 c.set_style(c.style_spec());
862 clang::RecordDecl *cls,
class_ &c,
const namespace_ &ns)
864 const auto *parent = cls->getParent();
866 std::optional<eid_t> id_opt;
869 if (parent !=
nullptr) {
870 const auto *parent_record_decl =
871 clang::dyn_cast<clang::RecordDecl>(parent);
873 if (parent_record_decl !=
nullptr) {
876 eid_t ast_id{parent_record_decl->getID()};
885 if (parent_record_decl->getDescribedTemplate() !=
nullptr) {
887 parent_record_decl->getDescribedTemplate()->getID();
894 if (id_opt &&
diagram().find<class_>(*id_opt)) {
901 const auto cls_name = cls->getNameAsString();
902 if (cls_name.empty()) {
905 const auto &[label, hint, access, destination_multiplicity] =
908 c.
set_name(parent_class.value().name() +
"##" +
909 fmt::format(
"({})", label));
911 std::string destination_multiplicity_str{};
912 if (destination_multiplicity.has_value()) {
913 destination_multiplicity_str =
914 std::to_string(*destination_multiplicity);
917 parent_class.value().add_relationship(
919 destination_multiplicity_str});
922 c.
set_name(parent_class.value().name() +
"##" +
924 "(anonymous_{})", std::to_string(cls->getID())));
928 parent_class.value().name() +
"##" + cls->getNameAsString());
933 if (!cls->getNameAsString().empty()) {
944 const clang::CXXRecordDecl &cls,
class_ &c)
956 const clang::CXXRecordDecl *cls,
class_ &c)
958 for (
const auto &base : cls->bases()) {
963 cp.
set_name(name_and_ns.to_string());
965 if (
const auto *tsp =
966 base.getType()->getAs<clang::TemplateSpecializationType>();
968 auto template_specialization_ptr =
969 std::make_unique<class_>(
config().using_namespace());
971 *template_specialization_ptr, cls, *tsp, {});
973 cp.
set_id(template_specialization_ptr->id());
974 cp.
set_name(template_specialization_ptr->full_name(
false));
977 add_class(std::move(template_specialization_ptr));
980 else if (
const auto *record_type =
981 base.getType()->getAs<clang::RecordType>();
982 record_type !=
nullptr) {
983 cp.
set_name(record_type->getDecl()->getQualifiedNameAsString());
995 LOG_DBG(
"Found base class {} [{}] for class {}", cp.
name(), cp.
id(),
1003 const clang::RecordDecl *cls,
class_ &c)
1006 for (
const auto *field : cls->fields()) {
1007 if (field !=
nullptr)
1013 const clang::CXXRecordDecl *cls,
class_ &c)
1015 assert(cls !=
nullptr);
1018 for (
const auto *method : cls->methods()) {
1019 if (method !=
nullptr) {
1025 if (
const auto *cls_decl_context =
1026 clang::dyn_cast_or_null<clang::DeclContext>(cls);
1027 cls_decl_context !=
nullptr) {
1028 for (
auto const *decl_iterator : cls_decl_context->decls()) {
1029 auto const *method_template =
1030 llvm::dyn_cast_or_null<clang::FunctionTemplateDecl>(
1032 if (method_template ==
nullptr)
1040 for (
const auto *field : cls->fields()) {
1041 if (field !=
nullptr)
1047 for (
const auto *decl : cls->decls()) {
1048 if (decl->getKind() == clang::Decl::Var) {
1049 const clang::VarDecl *variable_declaration{
1050 clang::dyn_cast_or_null<clang::VarDecl>(decl)};
1051 if ((variable_declaration !=
nullptr) &&
1052 variable_declaration->isStaticDataMember()) {
1056 else if (decl->getKind() == clang::Decl::Enum) {
1057 const auto *enum_decl =
1058 clang::dyn_cast_or_null<clang::EnumDecl>(decl);
1059 if (enum_decl ==
nullptr)
1062 if (enum_decl->getNameAsString().empty()) {
1063 for (
const auto *enum_const : enum_decl->enumerators()) {
1065 enum_decl->getAccess()),
1066 enum_const->getNameAsString(),
"enum"};
1073 if (cls->isCompleteDefinition())
1074 for (
const auto *friend_declaration : cls->friends()) {
1075 if (friend_declaration !=
nullptr)
1081 const clang::FriendDecl &f,
class_ &c)
1083 if (
const auto *friend_type_info = f.getFriendType()) {
1084 const auto friend_type = friend_type_info->getType();
1085 if (friend_type->getAs<clang::TemplateSpecializationType>() !=
1089 else if (friend_type->getAs<clang::RecordType>() !=
nullptr) {
1103 const clang::CXXMethodDecl &mf,
class_ &c)
1107 if (mf.isDefaulted() && !mf.isExplicitlyDefaulted())
1110 auto method_return_type =
1115 auto method_name = mf.getNameAsString();
1116 if (mf.isTemplated()) {
1120 method_name = method_name.substr(0, method_name.find(
'<'));
1125 config().simplify_template_type(method_return_type)};
1137 for (
const auto *param : mf.parameters()) {
1138 if (param !=
nullptr)
1143 found_relationships_t relationships;
1146 if (
const auto *templ = mf.getReturnType()
1147 .getNonReferenceType()
1148 .getUnqualifiedType()
1149 ->getAs<clang::TemplateSpecializationType>();
1151 const auto *unaliased_type = templ;
1152 if (unaliased_type->isTypeAlias())
1153 unaliased_type = unaliased_type->getAliasedType()
1154 ->getAs<clang::TemplateSpecializationType>();
1156 if (unaliased_type !=
nullptr) {
1157 auto template_specialization_ptr =
1158 std::make_unique<class_>(
config().using_namespace());
1160 *template_specialization_ptr,
1161 unaliased_type->getTemplateName().getAsTemplateDecl(),
1162 *unaliased_type, &c);
1165 relationships.emplace_back(template_specialization_ptr->id(),
1166 relationship_t::kDependency);
1168 add_class(std::move(template_specialization_ptr));
1174 mf.getReturnType(), relationships, relationship_t::kDependency);
1176 for (
const auto &[type_element_id, relationship_type] : relationships) {
1177 if (type_element_id != c.
id() &&
1178 (relationship_type != relationship_t::kNone)) {
1179 relationship r{relationship_t::kDependency, type_element_id};
1181 LOG_DBG(
"Adding method return type relationship from {}::{} to "
1193 auto underlying_type = mf.getReturnType();
1194 if (underlying_type->isReferenceType())
1195 underlying_type = underlying_type.getNonReferenceType();
1196 if (underlying_type->isPointerType())
1197 underlying_type = underlying_type->getPointeeType();
1199 if (
const auto *atsp = underlying_type->getAs<clang::AutoType>();
1204 method.update(
config().using_namespace());
1207 LOG_DBG(
"Adding method: {}", method.name());
1214 const clang::CXXMethodDecl &mf,
const class_ &c,
1215 const std::string &method_name,
class_method &method)
const
1217 const bool is_constructor = c.
name() == method_name;
1218 const bool is_destructor = fmt::format(
"~{}", c.
name()) == method_name;
1220#if LLVM_VERSION_MAJOR > 17
1221 method.is_pure_virtual(mf.isPureVirtual());
1223 method.is_pure_virtual(mf.isPure());
1225 method.is_virtual(mf.isVirtual());
1226 method.is_const(mf.isConst());
1227 method.is_defaulted(mf.isDefaulted());
1228 method.is_deleted(mf.isDeleted());
1229 method.is_static(mf.isStatic());
1230 method.is_operator(mf.isOverloadedOperator());
1231 method.is_constexpr(mf.isConstexprSpecified() && !is_constructor);
1232 method.is_consteval(mf.isConsteval());
1233 method.is_constructor(is_constructor);
1234 method.is_destructor(is_destructor);
1235 method.is_move_assignment(mf.isMoveAssignmentOperator());
1236 method.is_copy_assignment(mf.isCopyAssignmentOperator());
1237 method.is_noexcept(isNoexceptExceptionSpec(mf.getExceptionSpecType()));
1243 class_ &c,
const clang::AutoType *atsp)
1245 auto desugared_atsp = atsp->getDeducedType();
1247 if (atsp->isSugared()) {
1248 const auto *deduced_type =
1249 atsp->desugar()->getAs<clang::DeducedTemplateSpecializationType>();
1251 if (deduced_type !=
nullptr)
1252 desugared_atsp = deduced_type->getDeducedType();
1255 if (desugared_atsp.isNull())
1258 const auto *deduced_record_type = desugared_atsp->isRecordType()
1259 ? desugared_atsp->getAs<clang::RecordType>()
1262 if (deduced_record_type !=
nullptr) {
1263 if (
auto *deduced_auto_decl =
1264 llvm::dyn_cast_or_null<clang::ClassTemplateSpecializationDecl>(
1265 deduced_record_type->getDecl());
1266 deduced_auto_decl !=
nullptr) {
1268 const auto diagram_class_count_before_visit =
1273 const bool visitor_added_new_template_specialization =
1275 diagram_class_count_before_visit) > 0;
1277 if (visitor_added_new_template_specialization) {
1278 const auto &template_specialization_model =
1283 template_specialization_model.get().id()};
1293 const clang::FunctionTemplateDecl &mf,
class_ &c)
1297 if (mf.getTemplatedDecl()->isDefaulted() &&
1298 !mf.getTemplatedDecl()->isExplicitlyDefaulted())
1303 mf.getTemplatedDecl()->getReturnType().getAsString()};
1305 auto method_name = mf.getNameAsString();
1306 if (mf.isTemplated()) {
1310 method_name = method_name.substr(0, method_name.find(
'<'));
1313 clang::dyn_cast<clang::CXXMethodDecl>(mf.getTemplatedDecl()),
1314 [&](
const auto *decl) {
1315 process_method_properties(*decl, c, method_name, method);
1325 for (
const auto *param : mf.getTemplatedDecl()->parameters()) {
1326 if (param !=
nullptr)
1330 method.update(
config().using_namespace());
1333 LOG_DBG(
"Adding method: {}", method.name());
1340 found_relationships_t &relationships,
1345 if (type->isPointerType()) {
1346 relationship_hint = relationship_t::kAssociation;
1348 type->getPointeeType(), relationships, relationship_hint);
1350 else if (type->isRValueReferenceType()) {
1351 relationship_hint = relationship_t::kAggregation;
1353 type.getNonReferenceType(), relationships, relationship_hint);
1355 else if (type->isLValueReferenceType()) {
1356 relationship_hint = relationship_t::kAssociation;
1358 type.getNonReferenceType(), relationships, relationship_hint);
1360 else if (type->isArrayType()) {
1362 relationships, relationship_t::kAggregation);
1364 else if (type->isEnumeralType()) {
1365 if (
const auto *enum_type = type->getAs<clang::EnumType>();
1366 enum_type !=
nullptr) {
1370 relationships.emplace_back(
1371 enum_type->getDecl()->getID(), relationship_hint);
1374 else if (type->isRecordType()) {
1375 const auto *type_instantiation_decl =
1376 type->getAs<clang::TemplateSpecializationType>();
1378 if (type_instantiation_decl !=
nullptr) {
1382 .getAsTemplateDecl())) {
1383 relationships.emplace_back(
1384 type_instantiation_decl->getTemplateName()
1385 .getAsTemplateDecl()
1389 for (
const auto &template_argument :
1390 type_instantiation_decl->template_arguments()) {
1391 const auto template_argument_kind = template_argument.getKind();
1392 if (template_argument_kind ==
1393 clang::TemplateArgument::ArgKind::Integral) {
1396 else if (template_argument_kind ==
1397 clang::TemplateArgument::ArgKind::Null) {
1400 else if (template_argument_kind ==
1401 clang::TemplateArgument::ArgKind::Expression) {
1404 else if (template_argument.getKind() ==
1405 clang::TemplateArgument::ArgKind::NullPtr) {
1408 else if (template_argument_kind ==
1409 clang::TemplateArgument::ArgKind::Template) {
1412 else if (template_argument_kind ==
1413 clang::TemplateArgument::ArgKind::TemplateExpansion) {
1416 else if (
const auto *function_type =
1417 template_argument.getAsType()
1418 ->getAs<clang::FunctionProtoType>();
1419 function_type !=
nullptr) {
1420 for (
const auto ¶m_type :
1421 function_type->param_types()) {
1423 relationship_t::kDependency);
1426 else if (template_argument_kind ==
1427 clang::TemplateArgument::ArgKind::Type) {
1429 relationships, relationship_hint);
1433 else if (type->getAsCXXRecordDecl() !=
nullptr) {
1434 relationships.emplace_back(
1435 type->getAsCXXRecordDecl()->getID(), relationship_hint);
1439 relationships.emplace_back(
1440 type->getAsRecordDecl()->getID(), relationship_hint);
1444 else if (
const auto *template_specialization_type =
1445 type->getAs<clang::TemplateSpecializationType>();
1446 template_specialization_type !=
nullptr) {
1447 if (
should_include(template_specialization_type->getTemplateName()
1448 .getAsTemplateDecl())) {
1449 relationships.emplace_back(
1450 template_specialization_type->getTemplateName()
1451 .getAsTemplateDecl()
1455 for (
const auto &template_argument :
1456 template_specialization_type->template_arguments()) {
1457 const auto template_argument_kind = template_argument.getKind();
1458 if (template_argument_kind ==
1459 clang::TemplateArgument::ArgKind::Integral) {
1462 else if (template_argument_kind ==
1463 clang::TemplateArgument::ArgKind::Null) {
1466 else if (template_argument_kind ==
1467 clang::TemplateArgument::ArgKind::Expression) {
1470 else if (template_argument.getKind() ==
1471 clang::TemplateArgument::ArgKind::NullPtr) {
1474 else if (template_argument_kind ==
1475 clang::TemplateArgument::ArgKind::Template) {
1478 else if (template_argument_kind ==
1479 clang::TemplateArgument::ArgKind::TemplateExpansion) {
1482 else if (
const auto *function_type =
1483 template_argument.getAsType()
1484 ->getAs<clang::FunctionProtoType>();
1485 function_type !=
nullptr) {
1486 for (
const auto ¶m_type : function_type->param_types()) {
1488 param_type, relationships, relationship_t::kDependency);
1491 else if (template_argument_kind ==
1492 clang::TemplateArgument::ArgKind::Type) {
1494 relationships, relationship_hint);
1504 const std::set<std::string> & )
1507 parameter.
set_name(p.getNameAsString());
1511 if (parameter.
skip())
1519 parameter.
set_type(parameter_type);
1521 if (p.hasDefaultArg()) {
1522 const auto *default_arg = p.getDefaultArg();
1523 if (default_arg !=
nullptr) {
1532 found_relationships_t relationships;
1534 LOG_DBG(
"Looking for relationships in type: {}",
1537 if (
const auto *templ =
1539 .getNonReferenceType()
1540 .getUnqualifiedType()
1541 ->getAs<clang::TemplateSpecializationType>();
1543 auto template_specialization_ptr =
1544 std::make_unique<class_>(
config().using_namespace());
1546 *template_specialization_ptr,
1547 templ->getTemplateName().getAsTemplateDecl(), *templ, &c);
1550 relationships.emplace_back(template_specialization_ptr->id(),
1551 relationship_t::kDependency);
1553 add_class(std::move(template_specialization_ptr));
1558 p.getType(), relationships, relationship_t::kDependency);
1560 for (
const auto &[type_element_id, relationship_type] : relationships) {
1561 if (type_element_id != c.
id() &&
1562 (relationship_type != relationship_t::kNone)) {
1563 relationship r{relationship_t::kDependency, type_element_id};
1565 LOG_DBG(
"Adding function parameter relationship from {} to "
1575 method.add_parameter(std::move(parameter));
1579 const class_member &field,
const found_relationships_t &relationships,
1580 bool break_on_first_aggregation)
1584 for (
const auto &[target, relationship_type] : relationships) {
1585 if (relationship_type != relationship_t::kNone) {
1588 r.set_access(field.
access());
1589 bool mulitplicity_provided_in_comment{
false};
1590 if (decorator_rtype != relationship_t::kNone) {
1591 r.set_type(decorator_rtype);
1592 auto mult =
util::split(decorator_rmult,
":",
false);
1593 if (mult.size() == 2) {
1594 mulitplicity_provided_in_comment =
true;
1595 r.set_multiplicity_source(mult[0]);
1596 r.set_multiplicity_destination(mult[1]);
1599 if (!mulitplicity_provided_in_comment &&
1601 r.set_multiplicity_destination(
1607 LOG_DBG(
"Adding relationship from {} to {} with label {}",
1613 if (break_on_first_aggregation &&
1614 relationship_type == relationship_t::kAggregation)
1621 const clang::VarDecl &field_declaration,
class_ &c)
1623 const auto field_type = field_declaration.getType();
1626 if (type_name.empty())
1627 type_name =
"<<anonymous>>";
1631 field_declaration.getNameAsString(),
1632 config().simplify_template_type(type_name)};
1634 field.is_static(
true);
1642 if (!field.skip_relationship()) {
1643 found_relationships_t relationships;
1647 relationship_t::kAssociation);
1655std::unique_ptr<class_>
1657 clang::ClassTemplateSpecializationDecl *cls)
1659 auto c_ptr = std::make_unique<class_>(
config().using_namespace());
1662 auto &template_instantiation = *c_ptr;
1663 template_instantiation.is_template(
true);
1666 auto qualified_name = cls->getQualifiedNameAsString();
1670 namespace_ ns{qualified_name};
1672 template_instantiation.set_name(cls->getNameAsString());
1673 template_instantiation.set_namespace(ns);
1675 template_instantiation.is_struct(cls->isStruct());
1679 if (!template_instantiation.is_nested()) {
1681 template_instantiation.set_id(
1689 if (template_instantiation.skip())
1692 id_mapper().
add(cls->getID(), template_instantiation.id());
1698 const clang::FieldDecl &field_declaration,
class_ &c)
1701 "== Visiting record member {}", field_declaration.getNameAsString());
1704 auto relationship_hint = relationship_t::kAggregation;
1708 [[maybe_unused]]
bool template_instantiation_added_as_aggregation{
false};
1710 auto field_type = field_declaration.getType();
1715 const auto field_name = field_declaration.getNameAsString();
1717 auto field_type_str =
1724 field_name,
config().simplify_template_type(field_type_str)};
1735 if (field_type->isPointerType()) {
1736 relationship_hint = relationship_t::kAssociation;
1737 field_type = field_type->getPointeeType();
1739 else if (field_type->isLValueReferenceType()) {
1740 relationship_hint = relationship_t::kAssociation;
1741 field_type = field_type.getNonReferenceType();
1743 else if (field_type->isArrayType()) {
1744 relationship_hint = relationship_t::kAggregation;
1745 while (field_type->isArrayType()) {
1746 auto current_multiplicity = field.destination_multiplicity();
1747 if (!current_multiplicity)
1749 *field_type->getAsArrayTypeUnsafe()));
1751 auto maybe_array_size =
1753 if (maybe_array_size.has_value()) {
1754 field.set_destination_multiplicity(
1755 current_multiplicity.value() *
1756 maybe_array_size.value());
1760 field_type = field_type->getAsArrayTypeUnsafe()->getElementType();
1763 else if (field_type->isRValueReferenceType()) {
1764 field_type = field_type.getNonReferenceType();
1767 if (type_name.find(
"std::shared_ptr") == 0)
1768 relationship_hint = relationship_t::kAssociation;
1769 if (type_name.find(
"std::weak_ptr") == 0)
1770 relationship_hint = relationship_t::kAssociation;
1772 found_relationships_t relationships;
1774 const auto *template_field_type =
1775 field_type->getAs<clang::TemplateSpecializationType>();
1777 if (template_field_type !=
nullptr)
1778 if (template_field_type->isTypeAlias())
1779 template_field_type =
1780 template_field_type->getAliasedType()
1781 ->getAs<clang::TemplateSpecializationType>();
1783 bool field_type_is_template_template_parameter{
false};
1784 if (template_field_type !=
nullptr) {
1788 if (class_template_param.name() ==
1789 template_field_type->getTemplateName()
1790 .getAsTemplateDecl()
1791 ->getNameAsString() +
1793 field_type_is_template_template_parameter =
true;
1799 if (template_field_type !=
nullptr &&
1800 !field_type_is_template_template_parameter) {
1802 auto template_specialization_ptr =
1803 std::make_unique<class_>(
config().using_namespace());
1805 *template_specialization_ptr,
1806 field_type->getAs<clang::TemplateSpecializationType>()
1808 .getAsTemplateDecl(),
1809 *template_field_type, {&c});
1811 if (!field.skip_relationship() && template_specialization_ptr) {
1812 const auto &template_specialization = *template_specialization_ptr;
1818 bool add_template_instantiation_to_diagram{
false};
1820 template_specialization.get_namespace())) {
1822 found_relationships_t::value_type r{
1823 template_specialization.id(), relationship_hint};
1825 add_template_instantiation_to_diagram =
true;
1829 template_instantiation_added_as_aggregation =
1830 relationship_hint == relationship_t::kAggregation;
1831 relationships.emplace_back(std::move(r));
1836 found_relationships_t nested_relationships;
1837 if (!template_instantiation_added_as_aggregation) {
1838 for (
const auto &template_argument :
1839 template_specialization.template_params()) {
1841 LOG_DBG(
"Looking for nested relationships from {}::{} in "
1842 "template argument {}",
1844 template_argument.to_string(
1845 config().using_namespace(),
false));
1847 template_instantiation_added_as_aggregation =
1848 template_argument.find_nested_relationships(
1849 nested_relationships, relationship_hint,
1850 [&d =
diagram()](
const std::string &full_name) {
1851 if (full_name.empty())
1854 return d.should_include(ns, name);
1866 if (add_template_instantiation_to_diagram)
1867 add_class(std::move(template_specialization_ptr));
1871 if (!field.skip_relationship()) {
1874 if (!template_instantiation_added_as_aggregation) {
1875 if ((field_type->getAsRecordDecl() !=
nullptr) &&
1876 field_type->getAsRecordDecl()->getNameAsString().empty()) {
1881 std::make_tuple(field.name(), relationship_hint,
1882 field.access(), field.destination_multiplicity());
1886 field_type, relationships, relationship_hint);
1894 if ((field_type->getAsRecordDecl() !=
nullptr) &&
1895 field_type->getAsRecordDecl()->getNameAsString().empty()) {
1897 std::regex anonymous_re(
"anonymous_(\\d*)");
1899 std::regex_replace(field.type(), anonymous_re, field_name));
1909 if (
diagram().should_include(c->get_namespace())) {
1920 for (
const auto &cls :
diagram().classes()) {
1921 for (
auto &rel : cls.get().relationships()) {
1922 if (!rel.destination().is_global()) {
1923 const auto maybe_id =
1926 LOG_DBG(
"= Resolved instantiation destination from local "
1927 "id {} to global id {}",
1928 rel.destination(), *maybe_id);
1929 rel.set_destination(*maybe_id);
1934 for (
const auto &cpt :
diagram().concepts()) {
1935 for (
auto &rel : cpt.get().relationships()) {
1936 if (!rel.destination().is_global()) {
1937 const auto maybe_id =
1940 LOG_DBG(
"= Resolved instantiation destination from local "
1941 "id {} to global id {}",
1942 rel.destination(), *maybe_id);
1943 rel.set_destination(*maybe_id);
1948 for (
const auto &enm :
diagram().enums()) {
1949 for (
auto &rel : enm.get().relationships()) {
1950 if (!rel.destination().is_global()) {
1951 const auto maybe_id =
1954 LOG_DBG(
"= Resolved instantiation destination from local "
1955 "id {} to global id {}",
1956 rel.destination(), *maybe_id);
1957 rel.set_destination(*maybe_id);
1968 if (
config().skip_redundant_dependencies()) {
1969 diagram().remove_redundant_dependencies();
1974 const clang::ConceptSpecializationExpr *concept_specialization,
1975 const clang::ConceptDecl *cpt,
1976 std::vector<std::string> &constrained_template_params,
1977 size_t argument_index, std::string &type_name)
const
1982 if (!full_declaration_text.empty()) {
1984 if (type_name.find(
"type-parameter-") == 0) {
1985 const auto concept_declaration_text = full_declaration_text.substr(
1986 full_declaration_text.find(cpt->getNameAsString()) +
1987 cpt->getNameAsString().size() + 1);
1990 concept_declaration_text, [](
const auto &t) {
return t; });
1992 if (template_params.size() > argument_index)
1993 type_name = template_params[argument_index].to_string(
1994 config().using_namespace(),
false);
1996 constrained_template_params.push_back(type_name);
2001 std::string qualified_name)
2007 const std::string &qualified_name)
const
2013 std::unique_ptr<common::model::template_element> element)
2015 add_class(util::unique_pointer_cast<class_>(std::move(element)));
2020 if ((
config().generate_packages() &&
2022 assert(!c->file().empty());
2024 const auto file =
config().make_path_relative(c->file());
2030 diagram().add(p, std::move(c));
2032 else if ((
config().generate_packages() &&
2035 const auto module_path =
config().make_module_relative(c->module());
2039 diagram().add(p, std::move(c));
2042 diagram().add(c->path(), std::move(c));
2048 if ((
config().generate_packages() &&
2050 assert(!e->file().empty());
2052 const auto file =
config().make_path_relative(e->file());
2058 diagram().add(p, std::move(e));
2060 else if ((
config().generate_packages() &&
2063 const auto module_path =
config().make_module_relative(e->module());
2067 diagram().add(p, std::move(e));
2070 diagram().add(e->path(), std::move(e));
2076 if ((
config().generate_packages() &&
2078 assert(!c->file().empty());
2080 const auto file =
config().make_path_relative(c->file());
2086 diagram().add(p, std::move(c));
2088 else if ((
config().generate_packages() &&
2091 const auto module_path =
config().make_module_relative(c->module());
2095 diagram().add(p, std::move(c));
2098 diagram().add(c->path(), std::move(c));
2104 const std::string &full_name,
eid_t templated_decl_id)
2107 template_instantiation_base);
2111 std::string destination{};
2112 std::string best_match_full_name{};
2113 auto full_template_name = template_instantiation.
full_name(
false);
2115 eid_t best_match_id{};
2117 for (
const auto templ :
diagram().classes()) {
2118 if (templ.get() == template_instantiation)
2121 auto c_full_name = templ.get().full_name(
false);
2123 template_instantiation.calculate_template_specialization_match(
2126 if (match > best_match) {
2128 best_match_full_name = c_full_name;
2129 best_match_id = templ.get().id();
2133 auto templated_decl_global_id =
2136 if (best_match_id.value() > 0) {
2137 destination = best_match_full_name;
2138 template_instantiation.add_relationship(
2140 template_instantiation.template_specialization_found(
true);
2144 else if (
diagram().has_element(templated_decl_global_id)) {
2145 template_instantiation.add_relationship(
2147 templated_decl_global_id});
2148 template_instantiation.template_specialization_found(
true);
2151 LOG_DBG(
"Skipping instantiation relationship from {} to {}",
2152 template_instantiation.full_name(
false), templated_decl_global_id);
2155 LOG_DBG(
"== Cannot determine global id for specialization template {} "
2156 "- delaying until the translation unit is complete ",
2157 templated_decl_global_id);
2159 template_instantiation.add_relationship(