0.6.0
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
translation_unit_visitor.cc
Go to the documentation of this file.
1
2/**
3 * @file src/package_diagram/visitor/translation_unit_visitor.cc
4 *
5 * Copyright (c) 2021-2025 Bartek Kryza <bkryza@gmail.com>
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
21
22#include "common/clang_utils.h"
24
25#include "clang/Basic/Module.h"
26
27#include <spdlog/spdlog.h>
28
29#include <deque>
30
32
37
41 : visitor_specialization_t{sm, diagram, config}
42{
43}
44
46{
47 assert(ns != nullptr);
48
49 if (config().package_type() != config::package_type_t::kNamespace)
50 return true;
51
52 if (ns->isAnonymousNamespace() || ns->isInline())
53 return true;
54
55 auto qualified_name = common::get_qualified_name(*ns);
56
57 if (!diagram().should_include(namespace_{qualified_name}))
58 return true;
59
60 LOG_DBG("Visiting namespace declaration: {}", qualified_name);
61
62 auto package_path = namespace_{qualified_name};
63 auto package_parent = package_path;
64 bool is_root =
65 (package_path.size() == 1) && !config().using_namespace().is_empty();
66
67 std::string name;
68 if (!package_path.is_empty())
69 name = package_path.name();
70
71 if (!package_parent.is_empty())
72 package_parent.pop_back();
73
74 const auto usn = config().using_namespace();
75
76 auto p = std::make_unique<common::model::package>(usn);
77 package_path = package_path.relative_to(usn);
78
79 p->set_name(name);
80 p->set_namespace(package_parent);
81 p->is_root(is_root);
82 p->set_id(common::to_id(*ns));
83 set_source_location(*ns, *p);
84
85 assert(p->id().value() > 0);
86
87 if (diagram().should_include(*p) && !diagram().get(p->id())) {
88 process_comment(*ns, *p);
89
90 p->set_style(p->style_spec());
91
92 for (const auto *attr : ns->attrs()) {
93 if (attr->getKind() == clang::attr::Kind::Deprecated) {
94 p->set_deprecated(true);
95 break;
96 }
97 }
98
99 if (!p->skip()) {
100 LOG_DBG("Adding package {}", p->full_name(false));
101
102 diagram().add(p->path(), std::move(p));
103 }
104 }
105
106 return true;
107}
108
110 clang::FunctionDecl *function_declaration)
111{
112 assert(function_declaration != nullptr);
113
114 // Skip system headers
115 if (source_manager().isInSystemHeader(
116 function_declaration->getSourceRange().getBegin()))
117 return true;
118
119 found_relationships_t relationships;
120
121 find_relationships(function_declaration,
122 function_declaration->getReturnType(), relationships);
123
124 for (const auto *param : function_declaration->parameters()) {
125 if (param != nullptr)
127 function_declaration, param->getType(), relationships);
128 }
129
130 add_relationships(function_declaration, relationships);
131
132 return true;
133}
134
135bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls)
136{
137 assert(cls != nullptr);
138
139 // Skip system headers
140 if (source_manager().isInSystemHeader(cls->getSourceRange().getBegin()))
141 return true;
142
143 // Templated records are handled by VisitClassTemplateDecl()
144 if (cls->isTemplated() || cls->isTemplateDecl() ||
145 (clang::dyn_cast_or_null<clang::ClassTemplateSpecializationDecl>(cls) !=
146 nullptr))
147 return true;
148
149 found_relationships_t relationships;
150
151 if (cls->isCompleteDefinition()) {
152 process_class_declaration(*cls, relationships);
153 add_relationships(cls, relationships);
154 }
155
156 return true;
157}
158
159bool translation_unit_visitor::VisitRecordDecl(clang::RecordDecl *decl)
160{
161 assert(decl != nullptr);
162
163 // Skip system headers
164 if (source_manager().isInSystemHeader(decl->getSourceRange().getBegin()))
165 return true;
166
167 found_relationships_t relationships;
168
169 if (decl->isCompleteDefinition()) {
170 process_record_children(*decl, relationships);
171 add_relationships(decl, relationships);
172 }
173
174 return true;
175}
176
178{
179 assert(decl != nullptr);
180
181 // Skip system headers
182 if (source_manager().isInSystemHeader(decl->getSourceRange().getBegin()))
183 return true;
184
185 found_relationships_t relationships;
186
187 if (decl->isCompleteDefinition()) {
188 add_relationships(decl, relationships);
189 }
190
191 return true;
192}
193
195 clang::ClassTemplateDecl *decl)
196{
197 assert(decl != nullptr);
198
199 // Skip system headers
200 if (source_manager().isInSystemHeader(decl->getSourceRange().getBegin()))
201 return true;
202
203 found_relationships_t relationships;
204
205 util::if_not_null(decl->getTemplatedDecl(),
206 [this, &relationships, decl](const auto *template_decl) {
207 if (template_decl->isCompleteDefinition()) {
208 process_class_declaration(*template_decl, relationships);
209 add_relationships(decl, relationships);
210 }
211 });
212
213 return true;
214}
215
216bool translation_unit_visitor::VisitObjCCategoryDecl(
217 clang::ObjCCategoryDecl *decl)
218{
219 assert(decl != nullptr);
220
221 // Skip system headers
222 if (source_manager().isInSystemHeader(decl->getSourceRange().getBegin()))
223 return true;
224
225 found_relationships_t relationships;
226
227 const auto target_id = get_package_id(decl->getClassInterface());
228 relationships.emplace_back(target_id, relationship_t::kDependency, decl);
229
230 process_objc_container_children(*decl, relationships);
231 add_relationships(decl, relationships);
232
233 return true;
234}
235
236bool translation_unit_visitor::VisitObjCProtocolDecl(
237 clang::ObjCProtocolDecl *decl)
238{
239 assert(decl != nullptr);
240
241 // Skip system headers
242 if (source_manager().isInSystemHeader(decl->getSourceRange().getBegin()))
243 return true;
244
245 found_relationships_t relationships;
246
247 for (const auto *protocol : decl->protocols()) {
248 process_interface_protocol(*protocol, relationships);
249 }
250
251 process_objc_container_children(*decl, relationships);
252 add_relationships(decl, relationships);
253
254 return true;
255}
256
257bool translation_unit_visitor::VisitObjCInterfaceDecl(
258 clang::ObjCInterfaceDecl *decl)
259{
260 assert(decl != nullptr);
261
262 // Skip system headers
263 if (source_manager().isInSystemHeader(decl->getSourceRange().getBegin()))
264 return true;
265
266 found_relationships_t relationships;
267
268 for (const auto *protocol : decl->protocols()) {
269 process_interface_protocol(*protocol, relationships);
270 }
271
272 // Iterate over regular class fields
273 for (const auto *field : decl->ivars()) {
274 if (field != nullptr)
275 process_field(*field, relationships);
276 }
277
278 process_objc_container_children(*decl, relationships);
279
280 add_relationships(decl, relationships);
281
282 return true;
283}
284
285void translation_unit_visitor::add_relationships(
286 clang::Decl *cls, found_relationships_t &relationships)
287{
288 // If this diagram has directory packages, first make sure that the
289 // package for current directory is already in the model
290 if (config().package_type() == config::package_type_t::kDirectory) {
291 auto file = source_manager().getFilename(cls->getLocation()).str();
292
293 if (file.empty())
294 return;
295
296 auto relative_file = config().make_path_relative(file);
297 relative_file.make_preferred();
298
299 common::model::path parent_path{
300 relative_file.string(), common::model::path_type::kFilesystem};
301 parent_path.pop_back();
302 auto pkg_name = parent_path.name();
303 parent_path.pop_back();
304
305 auto pkg = std::make_unique<common::model::package>(
306 config().using_namespace(), common::model::path_type::kFilesystem);
307
308 pkg->set_name(pkg_name);
309 pkg->set_namespace(parent_path);
310 pkg->set_id(common::to_id(pkg->full_name(false)));
311 set_source_location(*cls, *pkg);
312
313 if (diagram().should_include(*pkg))
314 diagram().add(parent_path, std::move(pkg));
315 }
316 else if (config().package_type() == config::package_type_t::kModule) {
317 const auto *module = cls->getOwningModule();
318
319 if (module == nullptr) {
320 return;
321 }
322
323 std::string module_path_str = module->Name;
324#if LLVM_VERSION_MAJOR < 15
325 if (module->Kind == clang::Module::ModuleKind::PrivateModuleFragment) {
326#else
327 if (module->isPrivateModule()) {
328#endif
329 module_path_str = module->getTopLevelModule()->Name;
330 }
331
332 common::model::path module_path{
333 module_path_str, common::model::path_type::kModule};
334 module_path.pop_back();
335
336 auto relative_module =
337 config().make_module_relative(std::optional{module_path_str});
338
339 common::model::path parent_path{
340 relative_module, common::model::path_type::kModule};
341 auto pkg_name = parent_path.name();
342 parent_path.pop_back();
343
344 auto pkg = std::make_unique<common::model::package>(
345 config().using_module(), common::model::path_type::kModule);
346
347 pkg->set_name(pkg_name);
348 pkg->set_id(get_package_id(cls));
349 // This is for diagram filters
350 pkg->set_module(module_path.to_string());
351 // This is for rendering nested package structure
352 pkg->set_namespace(module_path);
353 set_source_location(*cls, *pkg);
354
355 if (diagram().should_include(*pkg))
356 diagram().add(parent_path, std::move(pkg));
357 }
358
359 auto current_package_id = get_package_id(cls);
360
361 if (current_package_id.value() == 0)
362 // These are relationships to a global namespace, and we don't care
363 // about those
364 return;
365
366 auto current_package = diagram().get(current_package_id);
367
368 if (current_package) {
369 std::vector<eid_t> parent_ids =
370 get_parent_package_ids(current_package_id);
371
372 for (auto &dependency : relationships) {
373 const auto destination_id = std::get<0>(dependency);
374
375 // Skip dependency relationships to parent packages
376 if (util::contains(parent_ids, destination_id))
377 continue;
378
379 // Skip dependency relationship to child packages
380 if (util::contains(
381 get_parent_package_ids(destination_id), current_package_id))
382 continue;
383
384 relationship r{relationship_t::kDependency, destination_id,
386
387 if (std::get<2>(dependency) != nullptr)
388 set_source_location(*std::get<2>(dependency), r);
389
390 if (destination_id != current_package_id)
391 current_package.value().add_relationship(std::move(r));
392 }
393 }
394}
395
396eid_t translation_unit_visitor::get_package_id(const clang::Decl *cls)
397{
398 if (config().package_type() == config::package_type_t::kNamespace) {
399 const auto *namespace_context =
400 cls->getDeclContext()->getEnclosingNamespaceContext();
401 if (namespace_context != nullptr && namespace_context->isNamespace()) {
402 return common::to_id(
403 *llvm::cast<clang::NamespaceDecl>(namespace_context));
404 }
405
406 return {};
407 }
408
409 if (config().package_type() == config::package_type_t::kModule) {
410 const auto *module = cls->getOwningModule();
411 if (module != nullptr) {
412 std::string module_path = module->Name;
413#if LLVM_VERSION_MAJOR < 15
414 if (module->Kind ==
415 clang::Module::ModuleKind::PrivateModuleFragment) {
416#else
417 if (module->isPrivateModule()) {
418#endif
419 module_path = module->getTopLevelModule()->Name;
420 }
421 return common::to_id(module_path);
422 }
423
424 return {};
425 }
426
427 auto file =
428 source_manager().getFilename(cls->getSourceRange().getBegin()).str();
429 auto relative_file = config().make_path_relative(file);
430 relative_file.make_preferred();
431 common::model::path parent_path{
432 relative_file.string(), common::model::path_type::kFilesystem};
433 parent_path.pop_back();
434
435 return common::to_id(parent_path.to_string());
436}
437
438void translation_unit_visitor::process_class_declaration(
439 const clang::CXXRecordDecl &cls, found_relationships_t &relationships)
440{
441 // Look for dependency relationships in class children (fields, methods)
442 process_class_children(cls, relationships);
443
444 // Look for dependency relationships in class bases
445 process_class_bases(cls, relationships);
446}
447
448void translation_unit_visitor::process_class_children(
449 const clang::CXXRecordDecl &cls, found_relationships_t &relationships)
450{
451 // Iterate over class methods (both regular and static)
452 for (const auto *method : cls.methods()) {
453 if (method != nullptr) {
454 process_method(*method, relationships);
455 }
456 }
457
458 if (const auto *decl_context =
459 clang::dyn_cast_or_null<clang::DeclContext>(&cls);
460 decl_context != nullptr) {
461 // Iterate over class template methods
462 for (auto const *decl_iterator : decl_context->decls()) {
463 auto const *method_template =
464 llvm::dyn_cast_or_null<clang::FunctionTemplateDecl>(
465 decl_iterator);
466 if (method_template == nullptr)
467 continue;
468
469 process_template_method(*method_template, relationships);
470 }
471 }
472
473 // Iterate over regular class fields
474 for (const auto *field : cls.fields()) {
475 if (field != nullptr)
476 process_field(*field, relationships);
477 }
478
479 // Static fields have to be processed by iterating over variable
480 // declarations
481 for (const auto *decl : cls.decls()) {
482 if (decl->getKind() == clang::Decl::Var) {
483 const clang::VarDecl *variable_declaration{
484 clang::dyn_cast_or_null<clang::VarDecl>(decl)};
485 if ((variable_declaration != nullptr) &&
486 variable_declaration->isStaticDataMember()) {
487 process_static_field(*variable_declaration, relationships);
488 }
489 }
490 }
491
492 if (cls.isCompleteDefinition())
493 for (const auto *friend_declaration : cls.friends()) {
494 if (friend_declaration != nullptr)
495 process_friend(*friend_declaration, relationships);
496 }
497}
498
499void translation_unit_visitor::process_class_bases(
500 const clang::CXXRecordDecl &cls, found_relationships_t &relationships)
501{
502 for (const auto &base : cls.bases()) {
503 find_relationships(&cls, base.getType(), relationships);
504 }
505}
506
507void translation_unit_visitor::process_method(
508 const clang::CXXMethodDecl &method, found_relationships_t &relationships)
509{
510 find_relationships(&method, method.getReturnType(), relationships);
511
512 for (const auto *param : method.parameters()) {
513 if (param != nullptr)
514 find_relationships(&method, param->getType(), relationships);
515 }
516}
517
518void translation_unit_visitor::process_objc_method(
519 const clang::ObjCMethodDecl &mf, found_relationships_t &relationships)
520{
521 find_relationships(&mf, mf.getReturnType(), relationships);
522
523 for (const auto *param : mf.parameters()) {
524 if (param != nullptr)
525 find_relationships(&mf, param->getType(), relationships);
526 }
527}
528
529void translation_unit_visitor::process_record_children(
530 const clang::RecordDecl &cls, found_relationships_t &relationships)
531{
532 if (const auto *decl_context =
533 clang::dyn_cast_or_null<clang::DeclContext>(&cls);
534 decl_context != nullptr) {
535 // Iterate over class template methods
536 for (auto const *decl_iterator : decl_context->decls()) {
537 auto const *method_template =
538 llvm::dyn_cast_or_null<clang::FunctionTemplateDecl>(
539 decl_iterator);
540 if (method_template == nullptr)
541 continue;
542
543 process_template_method(*method_template, relationships);
544 }
545 }
546
547 // Iterate over regular class fields
548 for (const auto *field : cls.fields()) {
549 if (field != nullptr)
550 process_field(*field, relationships);
551 }
552
553 // Static fields have to be processed by iterating over variable
554 // declarations
555 for (const auto *decl : cls.decls()) {
556 if (decl->getKind() == clang::Decl::Var) {
557 const clang::VarDecl *variable_declaration{
558 clang::dyn_cast_or_null<clang::VarDecl>(decl)};
559 if ((variable_declaration != nullptr) &&
560 variable_declaration->isStaticDataMember()) {
561 process_static_field(*variable_declaration, relationships);
562 }
563 }
564 }
565}
566
567void translation_unit_visitor::process_objc_container_children(
568 const clang::ObjCContainerDecl &cls,
570{
571 for (const auto *property : cls.properties()) {
572 if (property != nullptr)
573 process_objc_property(*property, relationships);
574 }
575
576 for (const auto *property : cls.class_properties()) {
577 if (property != nullptr)
578 process_objc_property(*property, relationships);
579 }
580
581 for (const auto *method : cls.methods()) {
582 if (method != nullptr)
583 process_objc_method(*method, relationships);
584 }
585
586 for (const auto *method : cls.class_methods()) {
587 if (method != nullptr)
588 process_objc_method(*method, relationships);
589 }
590
591 // Static fields have to be processed by iterating over variable
592 // declarations
593 for (const auto *decl : cls.decls()) {
594 if (decl->getKind() == clang::Decl::Var) {
595 const clang::VarDecl *variable_declaration{
596 clang::dyn_cast_or_null<clang::VarDecl>(decl)};
597 if ((variable_declaration != nullptr) &&
598 variable_declaration->isStaticDataMember()) {
599 process_static_field(*variable_declaration, relationships);
600 }
601 }
602 }
603}
604
605void translation_unit_visitor::process_template_method(
606 const clang::FunctionTemplateDecl &method,
607 found_relationships_t &relationships)
608{
609 // TODO: For now skip implicitly default methods
610 // in the future, add config option to choose
611 if (method.getTemplatedDecl()->isDefaulted() &&
612 !method.getTemplatedDecl()->isExplicitlyDefaulted())
613 return;
614
615 find_relationships(
616 &method, method.getTemplatedDecl()->getReturnType(), relationships);
617
618 for (const auto *param : method.getTemplatedDecl()->parameters()) {
619 if (param != nullptr) {
620 find_relationships(&method, param->getType(), relationships);
621 }
622 }
623}
624
625void translation_unit_visitor::process_field(
626 const clang::FieldDecl &field_declaration,
627 found_relationships_t &relationships)
628{
629 find_relationships(&field_declaration, field_declaration.getType(),
630 relationships, relationship_t::kDependency);
631}
632
633void translation_unit_visitor::process_interface_protocol(
634 const clang::ObjCProtocolDecl &protocol_declaration,
635 found_relationships_t &relationships)
636{
637 const auto target_id = get_package_id(&protocol_declaration);
638 relationships.emplace_back(
639 target_id, relationship_t::kDependency, &protocol_declaration);
640}
641
642void translation_unit_visitor::process_objc_property(
643 const clang::ObjCPropertyDecl &property_declaration,
644 found_relationships_t &relationships)
645{
646 const auto *property_objc_id_type =
647 property_declaration.getType()->getAsObjCQualifiedIdType();
648 if (property_objc_id_type != nullptr) {
649 const auto protocols_count = property_objc_id_type->getNumProtocols();
650 for (auto i = 0U; i < protocols_count; i++) {
651 const auto target_id =
652 get_package_id(property_objc_id_type->getProtocol(i));
653 relationships.emplace_back(
654 target_id, relationship_t::kDependency, &property_declaration);
655 }
656 }
657
658 find_relationships(&property_declaration, property_declaration.getType(),
659 relationships, relationship_t::kDependency);
660}
661
662void translation_unit_visitor::process_static_field(
663 const clang::VarDecl &field_declaration,
664 found_relationships_t &relationships)
665{
666 find_relationships(&field_declaration, field_declaration.getType(),
667 relationships, relationship_t::kDependency);
668}
669
670void translation_unit_visitor::process_friend(
671 const clang::FriendDecl &friend_declaration,
672 found_relationships_t &relationships)
673{
674 if (const auto *friend_type_declaration =
675 friend_declaration.getFriendDecl()) {
676 if (friend_type_declaration->isTemplateDecl()) {
677 // TODO
678 }
679 }
680 else if (const auto *friend_type = friend_declaration.getFriendType()) {
681 find_relationships(
682 &friend_declaration, friend_type->getType(), relationships);
683 }
684}
685
686bool translation_unit_visitor::find_relationships(const clang::Decl *decl,
687 const clang::QualType &type, found_relationships_t &relationships,
688 relationship_t relationship_hint)
689{
690 bool result{false};
691
692 if (type->isVoidType() || type->isVoidPointerType()) {
693 // pass
694 }
695 else if (type->isObjCObjectPointerType() &&
696 type->getPointeeType()->getAsObjCInterfaceType() != nullptr) {
697 const auto *objc_interface =
698 type->getPointeeType()->getAsObjCInterfaceType()->getInterface();
699 const auto target_id = get_package_id(objc_interface);
700 relationships.emplace_back(target_id, relationship_hint, decl);
701 result = true;
702 }
703 else if (type->isPointerType()) {
704 relationship_hint = relationship_t::kAssociation;
705 find_relationships(
706 decl, type->getPointeeType(), relationships, relationship_hint);
707 }
708 else if (type->isRValueReferenceType()) {
709 relationship_hint = relationship_t::kAggregation;
710 find_relationships(
711 decl, type.getNonReferenceType(), relationships, relationship_hint);
712 }
713 else if (type->isLValueReferenceType()) {
714 relationship_hint = relationship_t::kAssociation;
715 find_relationships(
716 decl, type.getNonReferenceType(), relationships, relationship_hint);
717 }
718 else if (type->isArrayType()) {
719 find_relationships(decl, type->getAsArrayTypeUnsafe()->getElementType(),
720 relationships, relationship_t::kAggregation);
721 }
722 else if (type->isEnumeralType()) {
723 if (const auto *enum_type = type->getAs<clang::EnumType>();
724 enum_type != nullptr) {
725 if (const auto *enum_decl = enum_type->getDecl();
726 enum_decl != nullptr)
727 relationships.emplace_back(
728 get_package_id(enum_decl), relationship_hint, decl);
729 }
730 }
731 else if (const auto *template_specialization_type =
732 type->getAs<clang::TemplateSpecializationType>()) {
733 if (template_specialization_type != nullptr) {
734 // Add dependency to template declaration
735 relationships.emplace_back(
736 get_package_id(template_specialization_type->getTemplateName()
737 .getAsTemplateDecl()),
738 relationship_hint, decl);
739
740 // Add dependencies to template arguments
741 for (const auto &template_argument :
742 template_specialization_type->template_arguments()) {
743 const auto template_argument_kind = template_argument.getKind();
744 if (template_argument_kind ==
745 clang::TemplateArgument::ArgKind::Integral) {
746 // pass
747 }
748 else if (template_argument_kind ==
749 clang::TemplateArgument::ArgKind::Null) {
750 // pass
751 }
752 else if (template_argument_kind ==
753 clang::TemplateArgument::ArgKind::Expression) {
754 // pass
755 }
756 else if (template_argument.getKind() ==
757 clang::TemplateArgument::ArgKind::NullPtr) {
758 // pass
759 }
760 else if (template_argument_kind ==
761 clang::TemplateArgument::ArgKind::Template) {
762 // pass
763 }
764 else if (template_argument_kind ==
765 clang::TemplateArgument::ArgKind::TemplateExpansion) {
766 // pass
767 }
768 else if (const auto *function_type =
769 template_argument.getAsType()
770 ->getAs<clang::FunctionProtoType>();
771 function_type != nullptr) {
772 for (const auto &param_type :
773 function_type->param_types()) {
774 result = find_relationships(decl, param_type,
775 relationships, relationship_t::kDependency);
776 }
777 }
778 else if (template_argument_kind ==
779 clang::TemplateArgument::ArgKind::Type) {
780 result =
781 find_relationships(decl, template_argument.getAsType(),
782 relationships, relationship_hint);
783 }
784 }
785 }
786 }
787 else if (type->isRecordType()) {
788 if (const auto *cxxrecord_decl = type->getAsCXXRecordDecl();
789 cxxrecord_decl != nullptr) {
790 if (config().package_type() == config::package_type_t::kNamespace) {
791 const auto *namespace_context =
792 cxxrecord_decl->getEnclosingNamespaceContext();
793 if (namespace_context != nullptr &&
794 namespace_context->isNamespace()) {
795 const auto *namespace_declaration =
796 clang::cast<clang::NamespaceDecl>(namespace_context);
797
798 if (namespace_declaration != nullptr &&
799 diagram().should_include(
801 *namespace_declaration)})) {
802 const auto target_id = get_package_id(cxxrecord_decl);
803 relationships.emplace_back(
804 target_id, relationship_hint, decl);
805 result = true;
806 }
807 }
808 }
809 else if (config().package_type() ==
811 const auto *module = cxxrecord_decl->getOwningModule();
812 if (module != nullptr) {
813 const auto target_id = get_package_id(cxxrecord_decl);
814 relationships.emplace_back(
815 target_id, relationship_hint, decl);
816 result = true;
817 }
818 }
819 else {
820 if (diagram().should_include(
822 *type->getAsCXXRecordDecl())})) {
823 const auto target_id =
824 get_package_id(type->getAsCXXRecordDecl());
825 relationships.emplace_back(
826 target_id, relationship_hint, decl);
827 result = true;
828 }
829 }
830 }
831 else if (const auto *record_decl = type->getAsRecordDecl();
832 record_decl != nullptr) {
833 // This is only possible for plain C translation unit, so we
834 // don't need to consider namespaces or modules here
835 if (config().package_type() == config::package_type_t::kDirectory) {
836 if (diagram().should_include(
837 namespace_{common::get_qualified_name(*record_decl)})) {
838 const auto target_id = get_package_id(record_decl);
839 relationships.emplace_back(
840 target_id, relationship_hint, decl);
841 result = true;
842 }
843 }
844 }
845 }
846
847 return result;
848}
849
850void translation_unit_visitor::finalize() { }
851
852std::vector<eid_t> translation_unit_visitor::get_parent_package_ids(eid_t id)
853{
854 std::vector<eid_t> parent_ids;
855 std::optional<eid_t> parent_id = id;
856
857 while (parent_id.has_value()) {
858 const auto pid = parent_id.value(); // NOLINT
859 parent_ids.push_back(pid);
860 auto parent = this->diagram().get(pid);
861 if (parent)
862 parent_id = parent.value().parent_element_id();
863 else
864 break;
865 }
866
867 return parent_ids;
868}
869
870} // namespace clanguml::package_diagram::visitor