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 * @file src/sequence_diagram/visitor/translation_unit_visitor.cc
3 *
4 * Copyright (c) 2021-2025 Bartek Kryza <bkryza@gmail.com>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
20
21#include "common/clang_utils.h"
24
26
30 : visitor_specialization_t{sm, diagram, config}
31 , template_builder_{diagram, config, *this}
32{
33}
34
35std::unique_ptr<sequence_diagram::model::class_>
37 const clang::NamedDecl * /*decl*/) const
38{
39 return std::make_unique<sequence_diagram::model::class_>(
40 config().using_namespace());
41}
42
44{
45 return true;
46}
47
49{
51}
52
54{
56}
57
59 clang::ObjCProtocolDecl *declaration)
60{
61 if (!should_include(declaration))
62 return true;
63
64 LOG_TRACE("Visiting ObjC interface declaration at {}",
65 declaration->getBeginLoc().printToString(source_manager()));
66
67 auto objc_protocol_model_ptr = create_objc_protocol_model(declaration);
68
69 if (!objc_protocol_model_ptr)
70 return true;
71
72 context().reset();
73
74 const auto class_id = objc_protocol_model_ptr->id();
75
76 set_unique_id(declaration->getID(), class_id);
77
78 auto &class_model =
79 diagram()
80 .get_participant<sequence_diagram::model::class_>(class_id)
81 .has_value()
82 ? *diagram()
83 .get_participant<sequence_diagram::model::class_>(class_id)
84 .get()
85 : *objc_protocol_model_ptr;
86
87 if (diagram().should_include(class_model)) {
88 LOG_DBG("Adding ObjC protocol participant {} with id {}",
89 class_model.full_name(false), class_model.id());
90
91 assert(class_model.id() == class_id);
92
93 context().set_caller_id(class_id);
94 context().update(declaration);
95
96 diagram().add_participant(std::move(objc_protocol_model_ptr));
97 }
98 else {
99 LOG_DBG("Skipping ObjC protocol {} with id {}",
100 class_model.full_name(true), class_id);
101 }
102
103 return true;
104}
105
107 clang::ObjCInterfaceDecl *declaration)
108{
109 if (!should_include(declaration))
110 return true;
111
112 LOG_TRACE("Visiting ObjC interface declaration at {}",
113 declaration->getBeginLoc().printToString(source_manager()));
114
115 // Build the class declaration and store it in the diagram, even
116 // if we don't need it for any of the participants of this diagram
117 auto objc_interface_model_ptr = create_objc_interface_model(declaration);
118
119 if (!objc_interface_model_ptr)
120 return true;
121
122 context().reset();
123
124 const auto class_id = objc_interface_model_ptr->id();
125
126 set_unique_id(declaration->getID(), class_id);
127
128 auto &class_model =
129 diagram()
130 .get_participant<sequence_diagram::model::class_>(class_id)
131 .has_value()
132 ? *diagram()
133 .get_participant<sequence_diagram::model::class_>(class_id)
134 .get()
135 : *objc_interface_model_ptr;
136
137 if (diagram().should_include(class_model)) {
138 LOG_DBG("Adding ObjC interface participant {} with id {}",
139 class_model.full_name(false), class_model.id());
140
141 assert(class_model.id() == class_id);
142
143 context().set_caller_id(class_id);
144 context().update(declaration);
145
146 diagram().add_participant(std::move(objc_interface_model_ptr));
147 }
148 else {
149 LOG_DBG("Skipping ObjC interface {} with id {}",
150 class_model.full_name(true), class_id);
151 }
152
153 return true;
154}
155
157 clang::CXXRecordDecl *declaration)
158{
159 if (!should_include(declaration))
160 return true;
161
162 // Skip this class if it's parent template is already in the model
163 if (declaration->isTemplated() &&
164 declaration->getDescribedTemplate() != nullptr) {
165 if (get_unique_id(eid_t{declaration->getDescribedTemplate()->getID()}))
166 return true;
167 }
168
169 // TODO: Add support for classes defined in function/method bodies
170 if (declaration->isLocalClass() != nullptr)
171 return true;
172
173 LOG_TRACE("Visiting class declaration at {}",
174 declaration->getBeginLoc().printToString(source_manager()));
175
176 // Build the class declaration and store it in the diagram, even
177 // if we don't need it for any of the participants of this diagram
178 auto class_model_ptr = create_class_model(declaration);
179
180 if (!class_model_ptr)
181 return true;
182
183 context().reset();
184
185 const auto class_id = class_model_ptr->id();
186
187 set_unique_id(declaration->getID(), class_id);
188
189 auto &class_model =
190 diagram()
191 .get_participant<sequence_diagram::model::class_>(class_id)
192 .has_value()
193 ? *diagram()
194 .get_participant<sequence_diagram::model::class_>(class_id)
195 .get()
196 : *class_model_ptr;
197
198 if (!declaration->isCompleteDefinition()) {
199 forward_declarations_.emplace(class_id, std::move(class_model_ptr));
200 return true;
201 }
202
203 forward_declarations_.erase(class_id);
204
205 if (diagram().should_include(class_model)) {
206 LOG_DBG("Adding class participant {} with id {}",
207 class_model.full_name(false), class_model.id());
208
209 assert(class_model.id() == class_id);
210
211 context().set_caller_id(class_id);
212 context().update(declaration);
213
214 diagram().add_participant(std::move(class_model_ptr));
215 }
216 else {
217 LOG_DBG("Skipping class {} with id {}", class_model.full_name(true),
218 class_id);
219 }
220
221 return true;
222}
223
225 clang::ClassTemplateDecl *declaration)
226{
227 if (!should_include(declaration))
228 return true;
229
230 LOG_TRACE("Visiting class template declaration {} at {} [{}]",
231 declaration->getQualifiedNameAsString(),
232 declaration->getLocation().printToString(source_manager()),
233 (void *)declaration);
234
235 auto class_model_ptr = create_class_model(declaration->getTemplatedDecl());
236
237 if (!class_model_ptr)
238 return true;
239
240 tbuilder().build_from_template_declaration(*class_model_ptr, *declaration);
241
242 const auto class_full_name = class_model_ptr->full_name(false);
243 const auto id = common::to_id(class_full_name);
244
245 // Override the id with the template id, for now we don't care about the
246 // underlying templated class id
247 class_model_ptr->set_id(id);
248
249 set_unique_id(declaration->getID(), id);
250
251 if (!declaration->getTemplatedDecl()->isCompleteDefinition()) {
252 forward_declarations_.emplace(id, std::move(class_model_ptr));
253 return true;
254 }
255 forward_declarations_.erase(id);
256
257 if (diagram().should_include(*class_model_ptr)) {
258 LOG_DBG("Adding class template participant {} with id {}",
259 class_full_name, id);
260
262 context().update(declaration);
263
264 diagram().add_participant(std::move(class_model_ptr));
265 }
266
267 return true;
268}
269
271 clang::ClassTemplateSpecializationDecl *declaration)
272{
273 if (!should_include(declaration))
274 return true;
275
276 LOG_TRACE("Visiting template specialization declaration {} at {}",
277 declaration->getQualifiedNameAsString(),
278 declaration->getLocation().printToString(source_manager()));
279
280 // TODO: Add support for classes defined in function/method bodies
281 if (declaration->isLocalClass() != nullptr)
282 return true;
283
284 auto template_specialization_ptr =
286
287 if (!template_specialization_ptr)
288 return true;
289
290 const auto class_full_name = template_specialization_ptr->full_name(false);
291 const auto id = common::to_id(class_full_name);
292
293 template_specialization_ptr->set_id(id);
294
295 set_unique_id(declaration->getID(), id);
296
297 if (!declaration->isCompleteDefinition()) {
298 forward_declarations_.emplace(
299 id, std::move(template_specialization_ptr));
300 return true;
301 }
302 forward_declarations_.erase(id);
303
304 if (diagram().should_include(*template_specialization_ptr)) {
305 LOG_DBG(
306 "Adding class template specialization participant {} with id {}",
307 class_full_name, id);
308
310 context().update(declaration);
311
312 diagram().add_participant(std::move(template_specialization_ptr));
313 }
314
315 return true;
316}
317
319 clang::ObjCMethodDecl *declaration)
320{
321 // We need to backup the context, since other methods or functions can
322 // be traversed during this traversal (e.g. template function/method
323 // specializations)
324 auto context_backup = context();
325
326 RecursiveASTVisitor<translation_unit_visitor>::TraverseObjCMethodDecl(
327 declaration);
328
329 call_expression_context_ = context_backup;
330
331 return true;
332}
333
335 clang::ObjCMethodDecl *declaration)
336{
337 if (!should_include(declaration))
338 return true;
339
340 LOG_TRACE("Visiting ObjC method {} in class",
341 declaration->getQualifiedNameAsString());
342
343 context().update(declaration);
344
345 auto method_model_ptr = create_objc_method_model(declaration);
346
347 if (!method_model_ptr)
348 return true;
349
350 process_comment(*declaration, *method_model_ptr);
351
352 set_source_location(*declaration, *method_model_ptr);
353
354 const auto method_full_name = method_model_ptr->full_name(false);
355
356 method_model_ptr->set_id(common::to_id(method_full_name));
357
358 set_unique_id(declaration->getID(), method_model_ptr->id());
359
360 LOG_TRACE("Set id {} --> {} for method name {} [{}]", declaration->getID(),
361 method_model_ptr->id(), method_full_name,
362 declaration->isThisDeclarationADefinition());
363
364 context().update(declaration);
365
366 context().set_caller_id(method_model_ptr->id());
367
368 diagram().add_participant(std::move(method_model_ptr));
369
370 return true;
371}
372
374 clang::CXXMethodDecl *declaration)
375{
376 // We need to backup the context, since other methods or functions can
377 // be traversed during this traversal (e.g. template function/method
378 // specializations)
379 auto context_backup = context();
380
381 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXMethodDecl(
382 declaration);
383
384 call_expression_context_ = context_backup;
385
386 return true;
387}
388
390 clang::CXXMethodDecl *declaration)
391{
392 if (!should_include(declaration))
393 return true;
394
395 if (!declaration->isThisDeclarationADefinition()) {
396 if (auto *declaration_definition = declaration->getDefinition();
397 declaration_definition != nullptr) {
398 if (auto *method_definition = clang::dyn_cast<clang::CXXMethodDecl>(
399 declaration_definition);
400 method_definition != nullptr) {
401 LOG_DBG("Calling VisitCXXMethodDecl recursively for forward "
402 "declaration");
403
404 return VisitCXXMethodDecl(method_definition);
405 }
406 }
407 }
408
409 LOG_TRACE("Visiting method {} in class {} [{}]",
410 declaration->getQualifiedNameAsString(),
411 declaration->getParent()->getQualifiedNameAsString(),
412 (void *)declaration->getParent());
413
414 context().update(declaration);
415
416 auto method_model_ptr = create_method_model(declaration);
417
418 if (!method_model_ptr)
419 return true;
420
421 process_comment(*declaration, *method_model_ptr);
422
423 set_source_location(*declaration, *method_model_ptr);
424
425 const auto method_full_name = method_model_ptr->full_name(false);
426
427 method_model_ptr->set_id(common::to_id(method_full_name));
428
429 // Callee methods in call expressions are referred to by first declaration
430 // id, so they should both be mapped to method_model
431 if (declaration->isThisDeclarationADefinition()) {
433 declaration->getFirstDecl()->getID(), method_model_ptr->id());
434 }
435
436 set_unique_id(declaration->getID(), method_model_ptr->id());
437
438 LOG_TRACE("Set id {} --> {} for method name {} [{}]", declaration->getID(),
439 method_model_ptr->id(), method_full_name,
440 declaration->isThisDeclarationADefinition());
441
442 context().update(declaration);
443
444 context().set_caller_id(method_model_ptr->id());
445
446 diagram().add_participant(std::move(method_model_ptr));
447
448 return true;
449}
450
452 clang::FunctionDecl *declaration)
453{
454 // We need to backup the context, since other methods or functions can
455 // be traversed during this traversal (e.g. template function/method
456 // specializations)
457 auto context_backup = context();
458
459 RecursiveASTVisitor<translation_unit_visitor>::TraverseFunctionDecl(
460 declaration);
461
462 call_expression_context_ = context_backup;
463
464 return true;
465}
466
468 clang::FunctionDecl *declaration)
469{
470 if (declaration->isCXXClassMember())
471 return true;
472
473 if (!should_include(declaration))
474 return true;
475
476 if (!declaration->isThisDeclarationADefinition()) {
477 if (auto *declaration_definition = declaration->getDefinition();
478 declaration_definition != nullptr)
479 return VisitFunctionDecl(
480 static_cast<clang::FunctionDecl *>(declaration_definition));
481 }
482
483 LOG_TRACE("Visiting function declaration {} at {}",
484 declaration->getQualifiedNameAsString(),
485 declaration->getLocation().printToString(source_manager()));
486
487 if (declaration->isTemplated()) {
488 if (declaration->getDescribedTemplate() != nullptr) {
489 // If the described templated of this function is already in the
490 // model skip it:
491 if (get_unique_id(
492 eid_t{declaration->getDescribedTemplate()->getID()}))
493 return true;
494 }
495 }
496
497 std::unique_ptr<model::function> function_model_ptr{};
498
499 if (declaration->isFunctionTemplateSpecialization()) {
500 function_model_ptr =
502 }
503 else {
504 function_model_ptr = build_function_model(*declaration);
505 }
506
507 if (!function_model_ptr)
508 return true;
509
510 function_model_ptr->set_id(
511 common::to_id(function_model_ptr->full_name(false)));
512
513 function_model_ptr->is_void(declaration->getReturnType()->isVoidType());
514
515 function_model_ptr->is_operator(declaration->isOverloadedOperator());
516
517 function_model_ptr->is_cuda_kernel(
518 common::has_attr(declaration, clang::attr::CUDAGlobal));
519
520 function_model_ptr->is_cuda_device(
521 common::has_attr(declaration, clang::attr::CUDADevice));
522
523 context().update(declaration);
524
525 context().set_caller_id(function_model_ptr->id());
526
527 if (declaration->isThisDeclarationADefinition()) {
529 declaration->getFirstDecl()->getID(), function_model_ptr->id());
530 }
531
532 set_unique_id(declaration->getID(), function_model_ptr->id());
533
534 process_comment(*declaration, *function_model_ptr);
535
536 set_source_location(*declaration, *function_model_ptr);
537
538 diagram().add_participant(std::move(function_model_ptr));
539
540 return true;
541}
542
544 clang::FunctionTemplateDecl *declaration)
545{
546 // We need to backup the context, since other methods or functions can
547 // be traversed during this traversal (e.g. template function/method
548 // specializations)
549 auto context_backup = context();
550
551 RecursiveASTVisitor<translation_unit_visitor>::TraverseFunctionTemplateDecl(
552 declaration);
553
554 call_expression_context_ = context_backup;
555
556 return true;
557}
558
560 clang::FunctionTemplateDecl *declaration)
561{
562 if (!should_include(declaration))
563 return true;
564
565 const auto function_name = declaration->getQualifiedNameAsString();
566
567 LOG_TRACE("Visiting function template declaration {} at {}", function_name,
568 declaration->getLocation().printToString(source_manager()));
569
570 auto function_template_model = build_function_template(*declaration);
571
572 process_comment(*declaration, *function_template_model);
573
574 set_source_location(*declaration, *function_template_model);
575 set_owning_module(*declaration, *function_template_model);
576
577 function_template_model->is_void(
578 declaration->getAsFunction()->getReturnType()->isVoidType());
579
580 function_template_model->set_id(
581 common::to_id(function_template_model->full_name(false)));
582
583 function_template_model->is_operator(
584 declaration->getAsFunction()->isOverloadedOperator());
585
586 context().update(declaration);
587
588 context().set_caller_id(function_template_model->id());
589
590 set_unique_id(declaration->getID(), function_template_model->id());
591
592 diagram().add_participant(std::move(function_template_model));
593
594 return true;
595}
596
597bool translation_unit_visitor::VisitLambdaExpr(clang::LambdaExpr *expr)
598{
599 if (!should_include(expr))
600 return true;
601
602 const auto lambda_full_name =
603 expr->getLambdaClass()->getCanonicalDecl()->getNameAsString();
604
605 LOG_TRACE("Visiting lambda expression {} at {} [caller_id = {}]",
606 lambda_full_name, expr->getBeginLoc().printToString(source_manager()),
607 context().caller_id());
608
609 LOG_TRACE("Lambda call operator ID {} - lambda class ID {}, class call "
610 "operator ID {}",
611 expr->getCallOperator()->getID(), expr->getLambdaClass()->getID(),
612 expr->getLambdaClass()->getLambdaCallOperator()->getID());
613
614 // Create lambda class participant
615 auto *cls = expr->getLambdaClass();
616 auto lambda_class_model_ptr = create_class_model(cls);
617
618 if (!lambda_class_model_ptr)
619 return true;
620
621 const auto cls_id = lambda_class_model_ptr->id();
622
623 set_unique_id(cls->getID(), cls_id);
624
625 auto lambda_method_model_ptr =
626 create_lambda_method_model(expr->getCallOperator());
627
628 lambda_method_model_ptr->set_class_id(cls_id);
629
630 // If this is a nested lambda, prepend the parent lambda name to this lambda
631 auto lambda_class_full_name = lambda_class_model_ptr->full_name(false);
632 lambda_method_model_ptr->set_class_full_name(lambda_class_full_name);
633
634 diagram().add_participant(std::move(lambda_class_model_ptr));
635
636 lambda_method_model_ptr->set_id(
637 common::to_id(get_participant(cls_id).value().full_name(false) +
638 "::" + lambda_method_model_ptr->full_name_no_ns()));
639
640 get_participant<model::class_>(cls_id).value().set_lambda_operator_id(
641 lambda_method_model_ptr->id());
642
643 // If lambda expression is in an argument to a method/function, and that
644 // method function would be excluded by filters
645 if (std::holds_alternative<clang::CallExpr *>(
646 context().current_callexpr()) &&
647 (!context().lambda_caller_id().has_value())) {
650
651 message m{message_t::kCall, context().caller_id()};
652 set_source_location(*expr, m);
653 m.set_from(context().caller_id());
654 m.set_to(lambda_method_model_ptr->id());
655
657
658 diagram().add_active_participant(m.from());
659 diagram().add_active_participant(m.to());
660
661 LOG_DBG("Found call {} from {} [{}] to {} [{}]", m.message_name(),
662 m.from(), m.from(), m.to(), m.to());
663
664 push_message(std::get<clang::CallExpr *>(context().current_callexpr()),
665 std::move(m));
666 }
667
668 context().enter_lambda_expression(lambda_method_model_ptr->id());
669
671 expr->getCallOperator()->getID(), lambda_method_model_ptr->id());
672
673 diagram().add_participant(std::move(lambda_method_model_ptr));
674
675 [[maybe_unused]] const auto is_generic_lambda = expr->isGenericLambda();
676
677 return true;
678}
679
681{
682 RecursiveASTVisitor<translation_unit_visitor>::TraverseLambdaExpr(expr);
683
684 // lambda context is entered inside the visitor
686
687 return true;
688}
689
691 clang::ObjCMessageExpr *expr)
692{
693 if (!config().include_system_headers() &&
694 source_manager().isInSystemHeader(expr->getSourceRange().getBegin()))
695 return true;
696
697 LOG_TRACE("Entering ObjC message expression at {}",
698 expr->getBeginLoc().printToString(source_manager()));
699
700 context().enter_callexpr(expr);
701
702 RecursiveASTVisitor<translation_unit_visitor>::TraverseObjCMessageExpr(
703 expr);
704
705 LOG_TRACE("Leaving ObjC message expression at {}",
706 expr->getBeginLoc().printToString(source_manager()));
707
709
711
712 return true;
713}
714
716{
717 if (!config().include_system_headers() &&
718 source_manager().isInSystemHeader(expr->getSourceRange().getBegin()))
719 return true;
720
721 LOG_TRACE("Entering call expression at {}",
722 expr->getBeginLoc().printToString(source_manager()));
723
724 context().enter_callexpr(expr);
725
726 RecursiveASTVisitor<translation_unit_visitor>::TraverseCallExpr(expr);
727
728 LOG_TRACE("Leaving call expression at {}",
729 expr->getBeginLoc().printToString(source_manager()));
730
732
734
735 return true;
736}
737
739 clang::CUDAKernelCallExpr *expr)
740{
741 if (!config().include_system_headers() &&
742 source_manager().isInSystemHeader(expr->getSourceRange().getBegin()))
743 return true;
744
745 LOG_TRACE("Entering CUDA kernel call expression at {}",
746 expr->getBeginLoc().printToString(source_manager()));
747
748 context().enter_callexpr(expr);
749
750 RecursiveASTVisitor<translation_unit_visitor>::TraverseCallExpr(expr);
751
752 LOG_TRACE("Leaving CUDA kernel call expression at {}",
753 expr->getBeginLoc().printToString(source_manager()));
754
756
758
759 return true;
760}
761
763 clang::CXXMemberCallExpr *expr)
764{
765 if (!config().include_system_headers() &&
766 source_manager().isInSystemHeader(expr->getSourceRange().getBegin()))
767 return true;
768
769 LOG_TRACE("Entering member call expression at {}",
770 expr->getBeginLoc().printToString(source_manager()));
771
772 context().enter_callexpr(expr);
773
774 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXMemberCallExpr(
775 expr);
776
777 LOG_TRACE("Leaving member call expression at {}",
778 expr->getBeginLoc().printToString(source_manager()));
779
781
783
784 return true;
785}
786
788 clang::CXXOperatorCallExpr *expr)
789{
790 context().enter_callexpr(expr);
791
792 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXOperatorCallExpr(
793 expr);
794
796
798
799 return true;
800}
801
803 clang::CXXTemporaryObjectExpr *expr)
804{
805 context().enter_callexpr(expr);
806
807 RecursiveASTVisitor<
809
811 clang::dyn_cast<clang::CXXConstructExpr>(expr));
812
814
816
817 return true;
818}
819
821 clang::CXXConstructExpr *expr)
822{
823 LOG_TRACE("Entering cxx construct call expression at {}",
824 expr->getBeginLoc().printToString(source_manager()));
825
826 context().enter_callexpr(expr);
827
828 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXConstructExpr(
829 expr);
830
832
833 LOG_TRACE("Leaving cxx construct call expression at {}",
834 expr->getBeginLoc().printToString(source_manager()));
835
837
839
840 return true;
841}
842
844{
849
850 if (stmt == nullptr)
851 return true;
852
853 const auto *current_ifstmt = context().current_ifstmt();
854 const auto *current_elseifstmt =
855 current_ifstmt != nullptr ? context().current_elseifstmt() : nullptr;
856
857 //
858 // Add final else block (not else if)
859 //
860 if (current_elseifstmt != nullptr) {
861 if (current_elseifstmt->getElse() == stmt) {
862 const auto current_caller_id = context().caller_id();
863
864 if (current_caller_id.value() != 0) {
865 model::message m{message_t::kElse, current_caller_id};
866 set_source_location(*stmt, m);
867 diagram().add_message(std::move(m));
868 }
869 }
870 }
871 else if (current_ifstmt != nullptr) {
872 if (current_ifstmt->getElse() == stmt) {
873 const auto current_caller_id = context().caller_id();
874
875 if (current_caller_id.value() != 0) {
876 model::message m{message_t::kElse, current_caller_id};
877 set_source_location(*stmt, m);
878 diagram().add_message(std::move(m));
879 }
880 }
881 }
882
883 RecursiveASTVisitor<translation_unit_visitor>::TraverseCompoundStmt(stmt);
884
885 return true;
886}
887
889{
894
895 bool elseif_block{false};
896
897 const auto current_caller_id = context().caller_id();
898 const auto *current_ifstmt = context().current_ifstmt();
899 const auto *current_elseifstmt =
900 current_ifstmt != nullptr ? context().current_elseifstmt() : nullptr;
901
902 std::string condition_text;
903 if (config().generate_condition_statements())
904 condition_text = common::get_condition_text(source_manager(), stmt);
905
906 // Check if this is a beginning of a new if statement, or an
907 // else if condition of the current if statement
908 auto child_stmt_compare = [stmt](auto *child_stmt) {
909 return child_stmt == stmt;
910 };
911
912 if (current_ifstmt != nullptr)
913 elseif_block = elseif_block ||
914 std::any_of(current_ifstmt->children().begin(),
915 current_ifstmt->children().end(), child_stmt_compare);
916 if (current_elseifstmt != nullptr)
917 elseif_block = elseif_block ||
918 std::any_of(current_elseifstmt->children().begin(),
919 current_elseifstmt->children().end(), child_stmt_compare);
920
921 if ((current_caller_id.value() != 0) && !stmt->isConstexpr()) {
922 if (elseif_block) {
924
925 message m{message_t::kElseIf, current_caller_id};
926 set_source_location(*stmt, m);
927 m.condition_text(condition_text);
929 *context().get_ast_context(), current_caller_id, stmt));
930 diagram().add_block_message(std::move(m));
931 }
932 else {
933 context().enter_ifstmt(stmt);
934 LOG_TRACE("Entered if statement at {}",
935 stmt->getBeginLoc().printToString(source_manager()));
936
937 message m{message_t::kIf, current_caller_id};
938 set_source_location(*stmt, m);
939 m.condition_text(condition_text);
941 *context().get_ast_context(), current_caller_id, stmt));
942 diagram().add_block_message(std::move(m));
943 }
944 }
945
946 RecursiveASTVisitor<translation_unit_visitor>::TraverseIfStmt(stmt);
947
948 if ((current_caller_id.value() != 0) && !stmt->isConstexpr()) {
949 if (!elseif_block) {
950 diagram().end_block_message(
951 {message_t::kIfEnd, current_caller_id}, message_t::kIf);
953 }
954 }
955
956 return true;
957}
958
960{
964
965 const auto current_caller_id = context().caller_id();
966
967 std::string condition_text;
968 if (config().generate_condition_statements())
969 condition_text = common::get_condition_text(source_manager(), stmt);
970
971 if (current_caller_id.value() != 0) {
972 LOG_TRACE("Entering while statement at {}",
973 stmt->getBeginLoc().printToString(source_manager()));
974
975 context().enter_loopstmt(stmt);
976 message m{message_t::kWhile, current_caller_id};
977 set_source_location(*stmt, m);
978 m.condition_text(condition_text);
980 *context().get_ast_context(), current_caller_id, stmt));
981 diagram().add_block_message(std::move(m));
982 }
983
984 RecursiveASTVisitor<translation_unit_visitor>::TraverseWhileStmt(stmt);
985
986 if (current_caller_id.value() != 0) {
987 diagram().end_block_message(
988 {message_t::kWhileEnd, current_caller_id}, message_t::kWhile);
990 }
991
992 return true;
993}
994
996{
1000
1001 const auto current_caller_id = context().caller_id();
1002
1003 std::string condition_text;
1004 if (config().generate_condition_statements())
1005 condition_text = common::get_condition_text(source_manager(), stmt);
1006
1007 if (current_caller_id.value() != 0) {
1008 context().enter_loopstmt(stmt);
1009 message m{message_t::kDo, current_caller_id};
1010 set_source_location(*stmt, m);
1011 m.condition_text(condition_text);
1012 m.set_comment(get_expression_comment(source_manager(),
1013 *context().get_ast_context(), current_caller_id, stmt));
1014 diagram().add_block_message(std::move(m));
1015 }
1016
1017 RecursiveASTVisitor<translation_unit_visitor>::TraverseDoStmt(stmt);
1018
1019 if (current_caller_id.value() != 0) {
1020 diagram().end_block_message(
1021 {message_t::kDoEnd, current_caller_id}, message_t::kDo);
1023 }
1024
1025 return true;
1026}
1027
1029{
1033
1034 const auto current_caller_id = context().caller_id();
1035
1036 std::string condition_text;
1037 if (config().generate_condition_statements())
1038 condition_text = common::get_condition_text(source_manager(), stmt);
1039
1040 if (current_caller_id.value() != 0) {
1041 context().enter_loopstmt(stmt);
1042 message m{message_t::kFor, current_caller_id};
1043 set_source_location(*stmt, m);
1044 m.condition_text(condition_text);
1045
1046 m.set_comment(get_expression_comment(source_manager(),
1047 *context().get_ast_context(), current_caller_id, stmt));
1048
1049 diagram().add_block_message(std::move(m));
1050 }
1051
1052 RecursiveASTVisitor<translation_unit_visitor>::TraverseForStmt(stmt);
1053
1054 if (current_caller_id.value() != 0) {
1055 diagram().end_block_message(
1056 {message_t::kForEnd, current_caller_id}, message_t::kFor);
1058 }
1059
1060 return true;
1061}
1062
1064{
1068
1069 const auto current_caller_id = context().caller_id();
1070
1071 if (current_caller_id.value() != 0) {
1072 context().enter_trystmt(stmt);
1073 message m{message_t::kTry, current_caller_id};
1074 set_source_location(*stmt, m);
1075 m.set_comment(get_expression_comment(source_manager(),
1076 *context().get_ast_context(), current_caller_id, stmt));
1077 diagram().add_block_message(std::move(m));
1078 }
1079
1080 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXTryStmt(stmt);
1081
1082 if (current_caller_id.value() != 0) {
1083 diagram().end_block_message(
1084 {message_t::kTryEnd, current_caller_id}, message_t::kTry);
1086 }
1087
1088 return true;
1089}
1090
1092{
1096
1097 const auto current_caller_id = context().caller_id();
1098
1099 if ((current_caller_id.value() != 0) &&
1100 (context().current_trystmt() != nullptr)) {
1101 std::string caught_type;
1102 if (stmt->getCaughtType().isNull())
1103 caught_type = "...";
1104 else
1105 caught_type = common::to_string(
1106 stmt->getCaughtType(), *context().get_ast_context());
1107
1108 model::message m{message_t::kCatch, current_caller_id};
1109 m.set_message_name(std::move(caught_type));
1110 diagram().add_message(std::move(m));
1111 }
1112
1113 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXCatchStmt(stmt);
1114
1115 return true;
1116}
1117
1119 clang::CXXForRangeStmt *stmt)
1120{
1124
1125 const auto current_caller_id = context().caller_id();
1126
1127 std::string condition_text;
1128 if (config().generate_condition_statements())
1129 condition_text = common::get_condition_text(source_manager(), stmt);
1130
1131 if (current_caller_id.value() != 0) {
1132 context().enter_loopstmt(stmt);
1133 message m{message_t::kFor, current_caller_id};
1134 set_source_location(*stmt, m);
1135 m.condition_text(condition_text);
1136 m.set_comment(get_expression_comment(source_manager(),
1137 *context().get_ast_context(), current_caller_id, stmt));
1138 diagram().add_block_message(std::move(m));
1139 }
1140
1141 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXForRangeStmt(
1142 stmt);
1143
1144 if (current_caller_id.value() != 0) {
1145 diagram().end_block_message(
1146 {message_t::kForEnd, current_caller_id}, message_t::kFor);
1148 }
1149
1150 return true;
1151}
1152
1154{
1156
1157 const auto current_caller_id = context().caller_id();
1158
1159 if (current_caller_id.value() != 0) {
1160 context().enter_switchstmt(stmt);
1161 model::message m{message_t::kSwitch, current_caller_id};
1162 set_source_location(*stmt, m);
1163 m.set_comment(get_expression_comment(source_manager(),
1164 *context().get_ast_context(), current_caller_id, stmt));
1165 diagram().add_block_message(std::move(m));
1166 }
1167
1168 RecursiveASTVisitor<translation_unit_visitor>::TraverseSwitchStmt(stmt);
1169
1170 if (current_caller_id.value() != 0) {
1172 diagram().end_block_message(
1173 {message_t::kSwitchEnd, current_caller_id}, message_t::kSwitch);
1174 }
1175
1176 return true;
1177}
1178
1180{
1182
1183 const auto current_caller_id = context().caller_id();
1184
1185 if ((current_caller_id.value() != 0) &&
1186 (context().current_switchstmt() != nullptr)) {
1187 model::message m{message_t::kCase, current_caller_id};
1188 m.set_message_name(common::to_string(stmt->getLHS()));
1189 diagram().add_case_stmt_message(std::move(m));
1190 }
1191
1192 RecursiveASTVisitor<translation_unit_visitor>::TraverseCaseStmt(stmt);
1193
1194 return true;
1195}
1196
1198{
1200
1201 const auto current_caller_id = context().caller_id();
1202
1203 if ((current_caller_id.value() != 0) &&
1204 (context().current_switchstmt() != nullptr)) {
1205 model::message m{message_t::kCase, current_caller_id};
1206 m.set_message_name("default");
1207 diagram().add_case_stmt_message(std::move(m));
1208 }
1209
1210 RecursiveASTVisitor<translation_unit_visitor>::TraverseDefaultStmt(stmt);
1211
1212 return true;
1213}
1214
1216 clang::ConditionalOperator *stmt)
1217{
1219
1220 const auto current_caller_id = context().caller_id();
1221
1222 std::string condition_text;
1223 if (config().generate_condition_statements())
1224 condition_text = common::get_condition_text(source_manager(), stmt);
1225
1226 if (current_caller_id.value() != 0) {
1228 model::message m{message_t::kConditional, current_caller_id};
1229 set_source_location(*stmt, m);
1230 m.condition_text(condition_text);
1231 m.set_comment(get_expression_comment(source_manager(),
1232 *context().get_ast_context(), current_caller_id, stmt));
1233 diagram().add_block_message(std::move(m));
1234 }
1235
1236 RecursiveASTVisitor<translation_unit_visitor>::TraverseStmt(
1237 stmt->getCond());
1238
1239 RecursiveASTVisitor<translation_unit_visitor>::TraverseStmt(
1240 stmt->getTrueExpr());
1241
1242 if (current_caller_id.value() != 0) {
1243 model::message m{message_t::kConditionalElse, current_caller_id};
1244 set_source_location(*stmt, m);
1245 diagram().add_message(std::move(m));
1246 }
1247
1248 RecursiveASTVisitor<translation_unit_visitor>::TraverseStmt(
1249 stmt->getFalseExpr());
1250
1251 if (current_caller_id.value() != 0) {
1253 diagram().end_block_message(
1254 {message_t::kConditionalEnd, current_caller_id},
1255 message_t::kConditional);
1256 }
1257
1258 return true;
1259}
1260
1262 clang::ObjCMessageExpr *expr)
1263{
1269
1270 if (!context().valid() || context().get_ast_context() == nullptr)
1271 return true;
1272
1273 LOG_TRACE("Visiting ObjC message expression at {} [caller_id = {}]",
1274 expr->getBeginLoc().printToString(source_manager()),
1275 context().caller_id());
1276
1277 message m{message_t::kCall, context().caller_id()};
1278
1279 set_source_location(*expr, m);
1280
1281 const auto *raw_expr_comment = clanguml::common::get_expression_raw_comment(
1282 source_manager(), *context().get_ast_context(), expr);
1283 const auto stripped_comment = process_comment(
1284 raw_expr_comment, context().get_ast_context()->getDiagnostics(), m);
1285
1286 if (m.skip())
1287 return true;
1288
1289 auto generated_message_from_comment = generate_message_from_comment(m);
1290
1291 if (!generated_message_from_comment && !should_include(expr)) {
1292 LOG_DBG("Skipping call expression due to filter at: {}",
1293 expr->getBeginLoc().printToString(source_manager()));
1294
1295 processed_comments().erase(raw_expr_comment);
1296 return true;
1297 }
1298
1299 if (context().is_expr_in_current_control_statement_condition(expr)) {
1300 m.set_message_scope(common::model::message_scope_t::kCondition);
1301 }
1302
1303 if (generated_message_from_comment) {
1304 LOG_DBG(
1305 "Message for this call expression is taken from comment directive");
1306 return true;
1307 }
1308
1310
1311 // Add message to diagram
1312 if (m.from().value() > 0 && m.to().value() > 0) {
1313 if (raw_expr_comment != nullptr)
1314 m.set_comment(raw_expr_comment->getBeginLoc().getHashValue(),
1315 stripped_comment);
1316
1318
1319 diagram().add_active_participant(m.from());
1320 diagram().add_active_participant(m.to());
1321
1322 LOG_DBG("Found ObjC message {} from {} [{}] to {} [{}] ",
1323 m.message_name(), m.from(), m.from(), m.to(), m.to());
1324
1325 push_message(expr, std::move(m));
1326 }
1327
1328 return true;
1329}
1330
1332 clang::ObjCPropertyRefExpr *expr)
1333{
1339
1340 if (!context().valid() || context().get_ast_context() == nullptr)
1341 return true;
1342
1343 LOG_TRACE("Visiting ObjC property ref expression at {} [caller_id = {}]",
1344 expr->getBeginLoc().printToString(source_manager()),
1345 context().caller_id());
1346
1347 return true;
1348}
1349
1351{
1357
1358 if (!context().valid() || context().get_ast_context() == nullptr)
1359 return true;
1360
1361 LOG_TRACE("Visiting call expression at {} [caller_id = {}]",
1362 expr->getBeginLoc().printToString(source_manager()),
1363 context().caller_id());
1364
1365 message m{message_t::kCall, context().caller_id()};
1366
1367 m.in_static_declaration_context(within_static_variable_declaration_ > 0);
1368
1369 set_source_location(*expr, m);
1370
1371 const auto *raw_expr_comment = clanguml::common::get_expression_raw_comment(
1372 source_manager(), *context().get_ast_context(), expr);
1373 const auto stripped_comment = process_comment(
1374 raw_expr_comment, context().get_ast_context()->getDiagnostics(), m);
1375
1376 if (m.skip())
1377 return true;
1378
1379 auto generated_message_from_comment = generate_message_from_comment(m);
1380
1381 if (!generated_message_from_comment && !should_include(expr)) {
1382 LOG_DBG("Skipping call expression due to filter at: {}",
1383 expr->getBeginLoc().printToString(source_manager()));
1384
1385 processed_comments().erase(raw_expr_comment);
1386 return true;
1387 }
1388
1389 if (context().is_expr_in_current_control_statement_condition(expr)) {
1390 m.set_message_scope(common::model::message_scope_t::kCondition);
1391 }
1392
1393 if (generated_message_from_comment) {
1394 LOG_DBG(
1395 "Message for this call expression is taken from comment directive");
1396 }
1397 //
1398 // Call to a CUDA kernel function
1399 //
1400 else if (const auto *cuda_call_expr =
1401 clang::dyn_cast_or_null<clang::CUDAKernelCallExpr>(expr);
1402 cuda_call_expr != nullptr) {
1403 if (!process_cuda_kernel_call_expression(m, cuda_call_expr))
1404 return true;
1405 }
1406 //
1407 // Call to an overloaded operator
1408 //
1409 else if (const auto *operator_call_expr =
1410 clang::dyn_cast_or_null<clang::CXXOperatorCallExpr>(expr);
1411 operator_call_expr != nullptr) {
1412
1413 if (!process_operator_call_expression(m, operator_call_expr))
1414 return true;
1415 }
1416 //
1417 // Call to a class method
1418 //
1419 else if (const auto *method_call_expr =
1420 clang::dyn_cast_or_null<clang::CXXMemberCallExpr>(expr);
1421 method_call_expr != nullptr) {
1422
1423 if (!process_class_method_call_expression(m, method_call_expr))
1424 return true;
1425 }
1426 //
1427 // Call to function or template
1428 //
1429 else {
1430 auto *callee_decl = expr->getCalleeDecl();
1431
1432 if (callee_decl == nullptr) {
1433 LOG_DBG("Cannot get callee declaration - trying direct function "
1434 "callee...");
1435
1436 callee_decl = expr->getDirectCallee();
1437
1438 if (callee_decl != nullptr)
1439 LOG_DBG("Found function/method callee in: {}",
1440 common::to_string(expr));
1441 }
1442
1443 if (callee_decl == nullptr) {
1444 //
1445 // Call to a method of a class template
1446 //
1447 if (clang::dyn_cast_or_null<clang::CXXDependentScopeMemberExpr>(
1448 expr->getCallee()) != nullptr) {
1450 return true;
1451 }
1452 }
1453 //
1454 // Unresolved lookup expression are sometimes calls to template
1455 // functions
1456 //
1457 else if (clang::dyn_cast_or_null<clang::UnresolvedLookupExpr>(
1458 expr->getCallee()) != nullptr) {
1460 return true;
1461 }
1462 else if (clang::dyn_cast_or_null<clang::LambdaExpr>(
1463 expr->getCallee()) != nullptr) {
1464 LOG_DBG("Processing lambda expression callee");
1465 if (!process_lambda_call_expression(m, expr))
1466 return true;
1467 }
1468 else if (clang::dyn_cast_or_null<clang::DependentScopeDeclRefExpr>(
1469 expr->getCallee()) != nullptr) {
1470 LOG_DBG("Processing dependent scope declaration expression "
1471 "callee - not able to infer the template parameter "
1472 "type at this point: {}",
1473 expr->getBeginLoc().printToString(source_manager()));
1474 }
1475 else {
1476 LOG_DBG("Found unsupported callee decl type for: {} at {}",
1477 common::to_string(expr),
1478 expr->getBeginLoc().printToString(source_manager()));
1479 }
1480 }
1481 else {
1482 auto success = process_function_call_expression(m, expr);
1483
1484 if (!success) {
1485 LOG_DBG("Skipping call expression at: {}",
1486 expr->getBeginLoc().printToString(source_manager()));
1487
1488 return true;
1489 }
1490 }
1491 }
1492
1493 // Add message to diagram
1494 if (m.from().value() > 0 && m.to().value() > 0) {
1495 if (raw_expr_comment != nullptr)
1496 m.set_comment(raw_expr_comment->getBeginLoc().getHashValue(),
1497 stripped_comment);
1498
1500
1501 diagram().add_active_participant(m.from());
1502 diagram().add_active_participant(m.to());
1503
1504 LOG_DBG("Found call {} from {} [{}] to {} [{}] ", m.message_name(),
1505 m.from(), m.from(), m.to(), m.to());
1506
1507 push_message(expr, std::move(m));
1508 }
1509
1510 return true;
1511}
1512
1514{
1515 if (diagram().sequences().count(m.from()) == 0) {
1516 model::activity a{m.from()};
1517 diagram().sequences().insert({m.from(), std::move(a)});
1518 }
1519
1520 // Maintain reverse graph of activity callers
1521 activity_callers_[m.to()].emplace(m.from());
1522}
1523
1525 model::message &m) const
1526{
1527 auto generated_message_from_comment{false};
1528 for (const auto &decorator : m.decorators()) {
1529 auto call_decorator =
1530 std::dynamic_pointer_cast<decorators::call>(decorator);
1531 if (call_decorator &&
1532 call_decorator->applies_to_diagram(config().name)) {
1533 m.set_to(common::to_id(call_decorator->callee));
1534 generated_message_from_comment = true;
1535 break;
1536 }
1537 }
1538 return generated_message_from_comment;
1539}
1540
1542{
1543 if (decl->isStaticLocal())
1545
1546 RecursiveASTVisitor::TraverseVarDecl(decl);
1547
1548 if (decl->isStaticLocal())
1550
1551 return true;
1552}
1553
1555 clang::CXXConstructExpr *expr)
1556{
1562
1563 if (expr == nullptr)
1564 return true;
1565
1566 if (const auto *ctor = expr->getConstructor();
1567 ctor != nullptr && !should_include(ctor))
1568 return true;
1569
1570 LOG_TRACE("Visiting cxx construct expression at {} [caller_id = {}]",
1571 expr->getBeginLoc().printToString(source_manager()),
1572 context().caller_id());
1573
1574 message m{message_t::kCall, context().caller_id()};
1575
1576 m.in_static_declaration_context(within_static_variable_declaration_ > 0);
1577
1578 set_source_location(*expr, m);
1579
1580 if (context().is_expr_in_current_control_statement_condition(expr)) {
1581 m.set_message_scope(common::model::message_scope_t::kCondition);
1582 }
1583
1584 if (!process_construct_expression(m, expr))
1585 return true;
1586
1587 if (m.from().value() > 0 && m.to().value() > 0) {
1589
1590 diagram().add_active_participant(m.from());
1591 diagram().add_active_participant(m.to());
1592
1593 LOG_DBG("Found constructor call {} from {} [{}] to {} [{}] ",
1594 m.message_name(), m.from(), m.from(), m.to(), m.to());
1595
1596 push_message(expr, std::move(m));
1597 }
1598
1599 return true;
1600}
1601
1603 model::message &m, const clang::CUDAKernelCallExpr *expr)
1604{
1605 const auto *callee_decl = expr->getCalleeDecl();
1606
1607 if (callee_decl == nullptr)
1608 return false;
1609
1610 const auto *callee_function = callee_decl->getAsFunction();
1611
1612 if (callee_function == nullptr)
1613 return false;
1614
1615 if (!should_include(callee_function))
1616 return false;
1617
1618 // Skip free functions declared in files outside of included paths
1619 if (config().combine_free_functions_into_file_participants() &&
1621 return false;
1622
1623 auto callee_name = callee_function->getQualifiedNameAsString() + "()";
1624
1625 m.set_to(id_mapper().resolve_or(eid_t{callee_function->getID()}));
1626 m.set_message_name(callee_name.substr(0, callee_name.size() - 2));
1627
1628 return true;
1629}
1630
1632 model::message &m, const clang::CXXOperatorCallExpr *operator_call_expr)
1633{
1634 if (operator_call_expr->getCalleeDecl() == nullptr)
1635 return false;
1636
1637 LOG_DBG("Operator '{}' call expression to {} at {}",
1638 getOperatorSpelling(operator_call_expr->getOperator()),
1639 operator_call_expr->getCalleeDecl()->getID(),
1640 operator_call_expr->getBeginLoc().printToString(source_manager()));
1641
1642 // Handle the case if the callee is a lambda
1643 if (const auto *lambda_method = clang::dyn_cast<clang::CXXMethodDecl>(
1644 operator_call_expr->getCalleeDecl());
1645 lambda_method != nullptr && lambda_method->getParent()->isLambda()) {
1646
1647 LOG_DBG("Operator callee is a lambda: {}",
1648 common::to_string(lambda_method));
1649
1650 const auto source_location{
1651 lambda_source_location(lambda_method->getParent()->getLocation())};
1652
1653 auto lambda_name = make_lambda_name(lambda_method->getParent());
1654
1655 m.set_to(eid_t{lambda_method->getParent()->getID()});
1656 }
1657 else {
1658 const auto operator_ast_id =
1659 operator_call_expr->getCalleeDecl()->getID();
1660 m.set_to(id_mapper().resolve_or(eid_t{operator_ast_id}));
1661 }
1662
1663 m.set_message_name(fmt::format(
1664 "operator{}", getOperatorSpelling(operator_call_expr->getOperator())));
1665
1666 return true;
1667}
1668
1670 model::message &m, const clang::CXXConstructExpr *construct_expr)
1671{
1672 const auto *constructor = construct_expr->getConstructor();
1673 if (constructor == nullptr)
1674 return false;
1675
1676 const auto *constructor_parent = constructor->getParent();
1677 if (constructor_parent == nullptr)
1678 return false;
1679
1680 LOG_DBG("Constructor '{}' call expression to {} at {}",
1681 construct_expr->getConstructor()->getNameAsString(),
1682 constructor->getID(),
1683 construct_expr->getBeginLoc().printToString(source_manager()));
1684
1685 m.set_to(id_mapper().resolve_or(eid_t{constructor->getID()}));
1687 fmt::format("{}::{}", constructor_parent->getQualifiedNameAsString(),
1688 constructor_parent->getNameAsString()));
1689
1690 diagram().add_active_participant(eid_t{constructor->getID()});
1691
1692 return true;
1693}
1694
1696 model::message &m, const clang::ObjCMessageExpr *message_expr)
1697{
1698 const auto *method_decl = message_expr->getMethodDecl();
1699
1700 if (method_decl == nullptr)
1701 return false;
1702
1703 std::string method_name = method_decl->getQualifiedNameAsString();
1704
1705 if (message_expr->getReceiverInterface() == nullptr)
1706 return false;
1707
1708 const auto *callee_decl = message_expr->getReceiverInterface();
1709
1710 if (callee_decl == nullptr)
1711 return false;
1712
1713 if (!should_include(callee_decl) || !should_include(method_decl))
1714 return false;
1715
1716 if (callee_decl->getImplementation() != nullptr &&
1717 callee_decl->getImplementation()->getMethod(method_decl->getSelector(),
1718 method_decl->isInstanceMethod(), true) != nullptr) {
1719 const auto *impl_method_decl =
1720 callee_decl->getImplementation()->getMethod(
1721 method_decl->getSelector(), method_decl->isInstanceMethod(),
1722 true);
1723 m.set_to(eid_t{impl_method_decl->getID()});
1724 }
1725 else {
1726 m.set_to(eid_t{method_decl->getID()});
1727 }
1728
1729 m.set_message_name(method_decl->getNameAsString());
1731 message_expr->getCallReturnType(*context().get_ast_context())
1732 .getAsString());
1733
1734 LOG_TRACE("Set callee ObjC method id {} for method name {}", m.to(),
1735 method_decl->getQualifiedNameAsString());
1736
1737 diagram().add_active_participant(eid_t{method_decl->getID()});
1738
1739 return true;
1740}
1741
1743 model::message &m, const clang::CXXMemberCallExpr *method_call_expr)
1744{
1745 // Get callee declaration as methods parent
1746 const auto *method_decl = method_call_expr->getMethodDecl();
1747
1748 if (method_decl == nullptr)
1749 return false;
1750
1751 std::string method_name = method_decl->getQualifiedNameAsString();
1752
1753 const auto *callee_decl =
1754 method_decl != nullptr ? method_decl->getParent() : nullptr;
1755
1756 if (callee_decl == nullptr)
1757 return false;
1758
1759 if (!should_include(callee_decl) || !should_include(method_decl))
1760 return false;
1761
1762 m.set_to(eid_t{method_decl->getID()});
1763 m.set_message_name(method_decl->getNameAsString());
1765 method_call_expr->getCallReturnType(*context().get_ast_context())
1766 .getAsString());
1767
1768 LOG_TRACE("Set callee method id {} for method name {}", m.to(),
1769 method_decl->getQualifiedNameAsString());
1770
1771 diagram().add_active_participant(eid_t{method_decl->getID()});
1772
1773 return true;
1774}
1775
1777 model::message &m, const clang::CallExpr *expr)
1778{
1779 const auto *dependent_member_callee =
1780 clang::dyn_cast_or_null<clang::CXXDependentScopeMemberExpr>(
1781 expr->getCallee());
1782
1783 if (dependent_member_callee == nullptr)
1784 return false;
1785
1786 if (is_callee_valid_template_specialization(dependent_member_callee)) {
1787 if (const auto *tst = dependent_member_callee->getBaseType()
1788 ->getAs<clang::TemplateSpecializationType>();
1789 tst != nullptr) {
1790 const auto *template_declaration =
1791 tst->getTemplateName().getAsTemplateDecl();
1792
1793 std::string callee_method_full_name;
1794
1795 // First check if the primary template is already in the
1796 // participants map
1797 if (get_participant(template_declaration).has_value()) {
1798 callee_method_full_name = get_participant(template_declaration)
1799 .value()
1800 .full_name(false) +
1801 "::" + dependent_member_callee->getMember().getAsString();
1802
1803 for (const auto &[id, p] : diagram().participants()) {
1804 const auto p_full_name = p->full_name(false);
1805
1806 if (p_full_name.find(callee_method_full_name + "(") == 0) {
1807 // TODO: This selects the first matching template method
1808 // without considering arguments!!!
1809 m.set_to(id);
1810 break;
1811 }
1812 }
1813 }
1814 // Otherwise check if it is a smart pointer
1815 else if (is_smart_pointer(template_declaration)) {
1816 const auto *argument_template =
1817 template_declaration->getTemplateParameters()
1818 ->asArray()
1819 .front();
1820
1821 if (get_participant(argument_template).has_value()) {
1822 callee_method_full_name = get_participant(argument_template)
1823 .value()
1824 .full_name(false) +
1825 "::" +
1826 dependent_member_callee->getMember().getAsString();
1827
1828 for (const auto &[id, p] : diagram().participants()) {
1829 const auto p_full_name = p->full_name(false);
1830 if (p_full_name.find(callee_method_full_name + "(") ==
1831 0) {
1832 // TODO: This selects the first matching template
1833 // method without considering arguments!!!
1834 m.set_to(id);
1835 break;
1836 }
1837 }
1838 }
1839 else
1840 return false;
1841 }
1842
1844 dependent_member_callee->getMember().getAsString());
1845
1846 if (const auto maybe_id =
1847 get_unique_id(eid_t{template_declaration->getID()});
1848 maybe_id.has_value())
1849 diagram().add_active_participant(maybe_id.value());
1850 }
1851 }
1852 else {
1853 LOG_DBG("Skipping call due to unresolvable "
1854 "CXXDependentScopeMemberExpr at {}",
1855 expr->getBeginLoc().printToString(source_manager()));
1856 }
1857
1858 return true;
1859}
1860
1862 model::message &m, const clang::CallExpr *expr)
1863{
1864 const auto *callee_decl = expr->getCalleeDecl();
1865
1866 if (callee_decl == nullptr)
1867 return false;
1868
1869 const auto *callee_function = callee_decl->getAsFunction();
1870
1871 if (callee_function == nullptr)
1872 return false;
1873
1874 if (!should_include(callee_function))
1875 return false;
1876
1877 // Skip free functions declared in files outside of included paths
1878 if (config().combine_free_functions_into_file_participants() &&
1880 return false;
1881
1882 auto callee_name = callee_function->getQualifiedNameAsString() + "()";
1883
1884 m.set_to(id_mapper().resolve_or(eid_t{callee_function->getID()}));
1885 m.set_message_name(callee_name.substr(0, callee_name.size() - 2));
1886
1887 return true;
1888}
1889
1891 model::message &m, const clang::CallExpr *expr) const
1892{
1893 const auto *lambda_expr =
1894 clang::dyn_cast_or_null<clang::LambdaExpr>(expr->getCallee());
1895
1896 if (lambda_expr == nullptr)
1897 return true;
1898
1899 const auto lambda_class_id = eid_t{lambda_expr->getLambdaClass()->getID()};
1900 m.set_to(id_mapper().resolve_or(eid_t{lambda_class_id}));
1901
1902 return true;
1903}
1904
1906 model::message &m, const clang::CallExpr *expr) const
1907{
1908 // This is probably a template
1909 const auto *unresolved_expr =
1910 clang::dyn_cast_or_null<clang::UnresolvedLookupExpr>(expr->getCallee());
1911
1912 if (unresolved_expr != nullptr) {
1913 for (const auto *decl : unresolved_expr->decls()) {
1914 if (clang::dyn_cast_or_null<clang::FunctionTemplateDecl>(decl) !=
1915 nullptr) {
1916 const auto *ftd =
1917 clang::dyn_cast_or_null<clang::FunctionTemplateDecl>(decl);
1918 m.set_to(id_mapper().resolve_or(eid_t{ftd->getID()}));
1919 break;
1920 }
1921
1922 if (clang::dyn_cast_or_null<clang::FunctionDecl>(decl) != nullptr) {
1923 const auto *fd =
1924 clang::dyn_cast_or_null<clang::FunctionDecl>(decl);
1925 m.set_to(id_mapper().resolve_or(eid_t{fd->getID()}));
1926 break;
1927 }
1928
1929 LOG_DBG("Unknown unresolved lookup expression");
1930 }
1931 }
1932
1933 return true;
1934}
1935
1937 const clang::CXXDependentScopeMemberExpr *dependent_member_expr) const
1938{
1939 if (dependent_member_expr == nullptr)
1940 return false;
1941
1942 if (dependent_member_expr->getBaseType().isNull())
1943 return false;
1944
1945 const auto *tst = dependent_member_expr->getBaseType()
1946 ->getAs<clang::TemplateSpecializationType>();
1947
1948 if (tst == nullptr)
1949 return false;
1950
1951 return !(tst->isPointerType());
1952}
1953
1955 const clang::TemplateDecl *primary_template) const
1956{
1957 return primary_template->getQualifiedNameAsString().find(
1958 "std::unique_ptr") == 0 ||
1959 primary_template->getQualifiedNameAsString().find("std::shared_ptr") ==
1960 0 ||
1961 primary_template->getQualifiedNameAsString().find("std::weak_ptr") == 0;
1962}
1963
1964std::unique_ptr<clanguml::sequence_diagram::model::class_>
1966 clang::ObjCProtocolDecl *cls)
1967{
1968 assert(cls != nullptr);
1969
1970 auto c_ptr{std::make_unique<clanguml::sequence_diagram::model::class_>(
1971 config().using_namespace())};
1972 auto &c = *c_ptr;
1973
1974 auto qualified_name = cls->getQualifiedNameAsString();
1975
1976 c.is_objc_interface();
1977
1978 c.set_name(cls->getQualifiedNameAsString());
1979 c.set_id(common::to_id(*cls));
1980
1981 process_comment(*cls, c);
1982 set_source_location(*cls, c);
1983
1984 if (c.skip())
1985 return {};
1986
1987 c.set_style(c.style_spec());
1988
1989 return c_ptr;
1990}
1991
1992std::unique_ptr<clanguml::sequence_diagram::model::class_>
1994 clang::ObjCInterfaceDecl *cls)
1995{
1996 assert(cls != nullptr);
1997
1998 auto c_ptr{std::make_unique<clanguml::sequence_diagram::model::class_>(
1999 config().using_namespace())};
2000 auto &c = *c_ptr;
2001
2002 auto qualified_name = cls->getQualifiedNameAsString();
2003
2004 c.is_objc_interface(true);
2005
2006 c.set_name(cls->getQualifiedNameAsString());
2007 c.set_id(common::to_id(*cls));
2008
2009 process_comment(*cls, c);
2010 set_source_location(*cls, c);
2011
2012 if (c.skip())
2013 return {};
2014
2015 c.set_style(c.style_spec());
2016
2017 return c_ptr;
2018}
2019
2020std::unique_ptr<clanguml::sequence_diagram::model::class_>
2022{
2023 assert(cls != nullptr);
2024
2025 auto c_ptr{std::make_unique<clanguml::sequence_diagram::model::class_>(
2026 config().using_namespace())};
2027 auto &c = *c_ptr;
2028
2029 auto qualified_name = cls->getQualifiedNameAsString();
2030
2031 if (!cls->isLambda())
2032 if (!should_include(cls))
2033 return {};
2034
2035 auto ns = common::get_tag_namespace(*cls);
2036
2037 if (cls->isLambda() && !diagram().should_include(ns | "lambda"))
2038 return {};
2039
2040 const auto *parent = cls->getParent();
2041
2042 if ((parent != nullptr) && parent->isRecord()) {
2043 // Here we have 3 options, either:
2044 // - the parent is a regular C++ class/struct
2045 // - the parent is a class template declaration/specialization
2046 // - the parent is a lambda (i.e. this is a nested lambda expression)
2047 std::optional<eid_t> id_opt;
2048 const auto *parent_record_decl =
2049 clang::dyn_cast<clang::RecordDecl>(parent);
2050
2051 assert(parent_record_decl != nullptr);
2052
2053 const eid_t ast_id{parent_record_decl->getID()};
2054
2055 // First check if the parent has been added to the diagram as
2056 // regular class
2057 id_opt = get_unique_id(ast_id);
2058
2059 // If not, check if the parent template declaration is in the model
2060 if (!id_opt &&
2061 (parent_record_decl->getDescribedTemplate() != nullptr)) {
2062 parent_record_decl->getDescribedTemplate()->getID();
2063 if (parent_record_decl->getDescribedTemplate() != nullptr)
2064 id_opt = get_unique_id(ast_id);
2065 }
2066
2067 if (!id_opt)
2068 return {};
2069
2070 auto parent_class =
2071 diagram()
2073 *id_opt);
2074
2075 if (!parent_class) {
2076 return {};
2077 }
2078
2079 c.set_namespace(ns);
2080 if (cls->getNameAsString().empty()) {
2081 // Nested structs can be anonymous
2082 if (anonymous_struct_relationships_.count(cls->getID()) > 0) {
2083 const auto &[label, hint, access] =
2084 anonymous_struct_relationships_[cls->getID()];
2085
2086 c.set_name(parent_class.value().name() +
2087 "::" + fmt::format("({})", label));
2088
2089 parent_class.value().add_relationship(
2090 {hint, common::to_id(c.full_name(false)), access, label});
2091 }
2092 else
2093 c.set_name(parent_class.value().name() + "::" +
2094 fmt::format(
2095 "(anonymous_{})", std::to_string(cls->getID())));
2096 }
2097 else {
2098 c.set_name(
2099 parent_class.value().name() + "::" + cls->getNameAsString());
2100 }
2101
2102 c.set_id(common::to_id(c.full_name(false)));
2103
2104 c.nested(true);
2105 }
2106 else if (cls->isLambda()) {
2107 c.is_lambda(true);
2108 if (cls->getParent() != nullptr) {
2109 const auto type_name = make_lambda_name(cls);
2110
2111 c.set_name(type_name);
2112 c.set_namespace(ns);
2113 c.set_id(common::to_id(c.full_name(false)));
2114 }
2115 else {
2116 LOG_WARN("Cannot find parent declaration for lambda {}",
2117 cls->getQualifiedNameAsString());
2118 return {};
2119 }
2120 }
2121 else {
2122 c.set_name(common::get_tag_name(*cls));
2123 c.set_namespace(ns);
2124 c.set_id(common::to_id(c.full_name(false)));
2125 }
2126
2127 c.is_struct(cls->isStruct());
2128
2129 process_comment(*cls, c);
2130 set_source_location(*cls, c);
2131
2132 if (c.skip())
2133 return {};
2134
2135 c.set_style(c.style_spec());
2136
2137 return c_ptr;
2138}
2139
2140void translation_unit_visitor::set_unique_id(int64_t local_id, eid_t global_id)
2141{
2142 LOG_TRACE("Setting local element mapping {} --> {}", local_id, global_id);
2143
2144 assert(global_id.is_global());
2145
2146 id_mapper().add(local_id, global_id);
2147}
2148
2150 eid_t local_id) const
2151{
2152 if (local_id.is_global())
2153 return local_id;
2154
2155 return id_mapper().get_global_id(local_id);
2156}
2157
2158std::unique_ptr<model::function_template>
2160 const clang::FunctionTemplateDecl &declaration)
2161{
2162 auto function_template_model_ptr =
2163 std::make_unique<sequence_diagram::model::function_template>(
2164 config().using_namespace());
2165
2166 set_qualified_name(declaration, *function_template_model_ptr);
2167
2169 *function_template_model_ptr, declaration);
2170
2171 function_template_model_ptr->return_type(
2172 common::to_string(declaration.getAsFunction()->getReturnType(),
2173 declaration.getASTContext()));
2174
2175 for (const auto *param : declaration.getTemplatedDecl()->parameters()) {
2176 function_template_model_ptr->add_parameter(
2178 param->getType(), declaration.getASTContext(), false)));
2179 }
2180
2181 return function_template_model_ptr;
2182}
2183
2184std::unique_ptr<model::function_template>
2186 const clang::FunctionDecl &decl)
2187{
2188 auto template_instantiation_ptr =
2189 std::make_unique<model::function_template>(config().using_namespace());
2190 auto &template_instantiation = *template_instantiation_ptr;
2191
2192 set_qualified_name(decl, template_instantiation);
2193
2194 tbuilder().build(template_instantiation, &decl, decl.getPrimaryTemplate(),
2195 decl.getTemplateSpecializationArgs()->asArray(),
2196 common::to_string(&decl));
2197
2198 // Handle function parameters
2199 for (const auto *param : decl.parameters()) {
2200 template_instantiation_ptr->add_parameter(
2201 common::to_string(param->getType(), decl.getASTContext()));
2202 }
2203
2204 return template_instantiation_ptr;
2205}
2206
2207std::unique_ptr<model::function> translation_unit_visitor::build_function_model(
2208 const clang::FunctionDecl &declaration)
2209{
2210 auto function_model_ptr =
2211 std::make_unique<sequence_diagram::model::function>(
2212 config().using_namespace());
2213
2214 common::model::namespace_ ns{declaration.getQualifiedNameAsString()};
2215 function_model_ptr->set_name(ns.name());
2216 ns.pop_back();
2217 function_model_ptr->set_namespace(ns);
2218
2219 function_model_ptr->return_type(common::to_string(
2220 declaration.getReturnType(), declaration.getASTContext()));
2221
2222 for (const auto *param : declaration.parameters()) {
2223 function_model_ptr->add_parameter(
2225 param->getType(), declaration.getASTContext(), false)));
2226 }
2227
2228 if (declaration.isVariadic()) {
2229 function_model_ptr->add_parameter("...");
2230 }
2231
2232 return function_model_ptr;
2233}
2234
2235std::unique_ptr<model::class_>
2237 clang::ClassTemplateSpecializationDecl *cls)
2238{
2239 auto c_ptr{std::make_unique<model::class_>(config().using_namespace())};
2240
2242
2243 auto &template_instantiation = *c_ptr;
2244
2245 // TODO: refactor to method get_qualified_name()
2246 auto qualified_name = cls->getQualifiedNameAsString();
2247 util::replace_all(qualified_name, "(anonymous namespace)", "");
2248 util::replace_all(qualified_name, "::::", "::");
2249
2250 common::model::namespace_ ns{qualified_name};
2251 ns.pop_back();
2252 template_instantiation.set_name(cls->getNameAsString());
2253 template_instantiation.set_namespace(ns);
2254
2255 template_instantiation.is_struct(cls->isStruct());
2256
2257 process_comment(*cls, template_instantiation);
2258 set_source_location(*cls, template_instantiation);
2259 set_owning_module(*cls, template_instantiation);
2260
2261 if (template_instantiation.skip())
2262 return {};
2263
2264 template_instantiation.set_id(
2265 common::to_id(template_instantiation.full_name(false)));
2266
2267 set_unique_id(cls->getID(), template_instantiation.id());
2268
2269 return c_ptr;
2270}
2271
2273 const std::string &full_name) const
2274{
2275 return config().simplify_template_type(full_name);
2276}
2277
2279 const clang::SourceLocation &source_location) const
2280{
2281 const auto file_line =
2282 source_manager().getSpellingLineNumber(source_location);
2283 const auto file_column =
2284 source_manager().getSpellingColumnNumber(source_location);
2285 const std::string file_name =
2286 config()
2287 .make_path_relative(
2288 source_manager().getFilename(source_location).str())
2289 .string();
2290 return fmt::format("{}:{}:{}", file_name, file_line, file_column);
2291}
2292
2294 const clang::CXXRecordDecl *cls) const
2295{
2296 std::string result;
2297 const auto location = cls->getLocation();
2298 const std::string source_location{lambda_source_location(location)};
2299
2300 const auto maybe_lambda_caller_id = context().lambda_caller_id();
2301 if (maybe_lambda_caller_id.has_value()) {
2302 // Parent is also a lambda (this id points to a lambda operator())
2303 std::string parent_lambda_class_name{"()"};
2304 if (diagram().get_participant<model::method>(
2305 maybe_lambda_caller_id.value())) {
2306 auto parent_lambda_class_id =
2307 diagram()
2308 .get_participant<model::method>(
2309 maybe_lambda_caller_id.value())
2310 .value()
2311 .class_id();
2312
2313 if (diagram().get_participant<model::class_>(
2314 parent_lambda_class_id)) {
2315 parent_lambda_class_name =
2316 diagram()
2317 .get_participant<model::class_>(parent_lambda_class_id)
2318 .value()
2319 .full_name(false);
2320 }
2321 }
2322
2323 result = fmt::format(
2324 "{}##(lambda {})", parent_lambda_class_name, source_location);
2325 }
2326 else if (context().caller_id().value() != 0 &&
2327 get_participant(context().caller_id()).has_value()) {
2328 auto parent_full_name =
2329 get_participant(context().caller_id()).value().full_name_no_ns();
2330
2331 result =
2332 fmt::format("{}##(lambda {})", parent_full_name, source_location);
2333 }
2334 else {
2335 result = fmt::format("(lambda {})", source_location);
2336 }
2337
2338 return result;
2339}
2340
2342 clang::CallExpr *expr, model::message &&m)
2343{
2344 call_expr_message_map_[expr].push_back(std::move(m));
2345}
2346
2348 clang::CXXConstructExpr *expr, model::message &&m)
2349{
2350 construct_expr_message_map_.emplace(expr, std::move(m));
2351}
2352
2354 clang::ObjCMessageExpr *expr, model::message &&m)
2355{
2356 objc_message_map_.emplace(expr, std::move(m));
2357}
2358
2360{
2361 assert(expr != nullptr);
2362
2363 // Skip if no message was generated from this expr
2364 if (call_expr_message_map_.find(expr) == call_expr_message_map_.end()) {
2365 return;
2366 }
2367
2368 if (call_expr_message_map_.at(expr).empty())
2369 return;
2370
2371 while (!call_expr_message_map_.at(expr).empty()) {
2372 auto msg = call_expr_message_map_.at(expr).front();
2373
2374 auto caller_id = msg.from();
2375
2376 if (caller_id == 0)
2377 return;
2378
2379 if (diagram().has_activity(caller_id))
2380 diagram().get_activity(caller_id).add_message(std::move(msg));
2381 else
2382 LOG_DBG("Skipping message due to missing activity: {}", caller_id);
2383
2384 call_expr_message_map_.at(expr).pop_front();
2385 }
2386
2387 call_expr_message_map_.erase(expr);
2388}
2389
2391 clang::CXXConstructExpr *expr)
2392{
2393 assert(expr != nullptr);
2394
2395 // Skip if no message was generated from this expr
2396 if (construct_expr_message_map_.find(expr) ==
2398 return;
2399 }
2400
2401 auto msg = std::move(construct_expr_message_map_.at(expr));
2402
2403 auto caller_id = msg.from();
2404 diagram().get_activity(caller_id).add_message(std::move(msg));
2405
2406 construct_expr_message_map_.erase(expr);
2407}
2408
2410 clang::ObjCMessageExpr *expr)
2411{
2412 assert(expr != nullptr);
2413
2414 // Skip if no message was generated from this expr
2415 if (objc_message_map_.find(expr) == objc_message_map_.end()) {
2416 return;
2417 }
2418
2419 auto msg = std::move(objc_message_map_.at(expr));
2420
2421 auto caller_id = msg.from();
2422 diagram().get_activity(caller_id).add_message(std::move(msg));
2423
2424 objc_message_map_.erase(expr);
2425}
2426
2428{
2430
2431 // Change all messages with target set to an id of a lambda expression to
2432 // to the ID of their operator() - this is necessary, as some calls to
2433 // lambda expressions are visited before the actual lambda expressions
2434 // are visited...
2436
2438
2439 if (config().inline_lambda_messages())
2440 diagram().inline_lambda_operator_calls();
2441}
2442
2444{
2445 for (auto &[id, activity] : diagram().sequences()) {
2446 for (auto &m : activity.messages()) {
2447 auto participant = diagram().get_participant<model::class_>(m.to());
2448
2449 if (participant && participant.value().is_lambda() &&
2450 participant.value().lambda_operator_id().value() != 0) {
2451 LOG_DBG("Changing lambda expression target id from {} to {}",
2452 m.to(), participant.value().lambda_operator_id());
2453
2454 m.set_to(participant.value().lambda_operator_id());
2455 m.set_message_name("operator()");
2456
2458 }
2459 }
2460 }
2461}
2462
2464{
2465 std::set<eid_t> active_participants_unique;
2466
2467 // Change all active participants AST local ids to diagram global ids
2468 for (auto id : diagram().active_participants()) {
2469 if (const auto unique_id = get_unique_id(id);
2470 !id.is_global() && unique_id.has_value()) {
2471 active_participants_unique.emplace(unique_id.value());
2472 }
2473 else if (id.is_global()) {
2474 active_participants_unique.emplace(id);
2475 }
2476 }
2477
2478 diagram().active_participants() = std::move(active_participants_unique);
2479
2480 // Change all message callees AST local ids to diagram global ids
2481 for (auto &[id, activity] : diagram().sequences()) {
2482 for (auto &m : activity.messages()) {
2483 if (const auto unique_id = get_unique_id(m.to());
2484 !m.to().is_global() && unique_id.has_value()) {
2485 m.set_to(unique_id.value());
2486 assert(m.to().is_global());
2487 }
2488 }
2489 }
2490}
2491
2493{
2494 // Translate reverse activity call graph local ids to global ids
2495 std::map<eid_t, std::set<eid_t>> acs;
2496 for (const auto &[id, caller_ids] : activity_callers_) {
2497 auto unique_id = get_unique_id(id);
2498 if (!unique_id)
2499 continue;
2500 std::set<eid_t> unique_caller_ids;
2501 for (const auto &caller_id : caller_ids) {
2502 auto unique_caller_id = get_unique_id(caller_id);
2503 if (unique_caller_id)
2504 unique_caller_ids.emplace(*unique_caller_id);
2505 }
2506 acs.emplace(*unique_id, std::move(unique_caller_ids));
2507 }
2508
2509 // Change all message callees AST local ids to diagram global ids
2510 for (auto &[id, activity] : diagram().sequences()) {
2511 assert(id.is_global());
2512
2513 if (acs.count(id) > 0) {
2514 activity.set_callers(acs.at(id));
2515 }
2516 }
2517}
2518
2519std::unique_ptr<clanguml::sequence_diagram::model::method>
2521 clang::CXXMethodDecl *declaration)
2522{
2523 auto method_model_ptr = std::make_unique<sequence_diagram::model::method>(
2524 config().using_namespace());
2525
2526 common::model::namespace_ ns{declaration->getQualifiedNameAsString()};
2527 auto method_name = ns.name();
2528 method_model_ptr->set_method_name(method_name);
2529 ns.pop_back();
2530 method_model_ptr->set_name(ns.name());
2531 ns.pop_back();
2532 method_model_ptr->set_namespace(ns);
2533
2534 method_model_ptr->is_defaulted(declaration->isDefaulted());
2535 method_model_ptr->is_assignment(declaration->isCopyAssignmentOperator() ||
2536 declaration->isMoveAssignmentOperator());
2537 method_model_ptr->is_const(declaration->isConst());
2538 method_model_ptr->is_static(declaration->isStatic());
2539 method_model_ptr->is_operator(declaration->isOverloadedOperator());
2540 method_model_ptr->is_constructor(
2541 clang::dyn_cast<clang::CXXConstructorDecl>(declaration) != nullptr);
2542
2543 method_model_ptr->is_void(declaration->getReturnType()->isVoidType());
2544
2545 method_model_ptr->return_type(common::to_string(
2546 declaration->getReturnType(), declaration->getASTContext()));
2547
2548 for (const auto *param : declaration->parameters()) {
2549 auto parameter_type =
2550 common::to_string(param->getType(), param->getASTContext());
2552 parameter_type = simplify_system_template(parameter_type);
2553 method_model_ptr->add_parameter(config().using_namespace().relative(
2554 simplify_system_template(parameter_type)));
2555 }
2556
2557 return method_model_ptr;
2558}
2559
2560std::unique_ptr<clanguml::sequence_diagram::model::objc_method>
2562 clang::ObjCMethodDecl *declaration)
2563{
2564 auto method_model_ptr =
2565 std::make_unique<sequence_diagram::model::objc_method>(
2566 config().using_namespace());
2567
2568 common::model::namespace_ ns{declaration->getQualifiedNameAsString()};
2569 auto method_name = ns.name();
2570 method_model_ptr->set_method_name(method_name);
2571 ns.pop_back();
2572 method_model_ptr->set_name(ns.name());
2573 method_model_ptr->set_namespace({});
2574
2575 clang::Decl *parent_decl = declaration->getClassInterface();
2576
2577 if (parent_decl == nullptr) {
2578 LOG_DBG("Cannot find ObjC interface for method, probably it is a "
2579 "protocol {} [{}]",
2580 method_name,
2581 declaration->getLocation().printToString(source_manager()));
2582 return {};
2583 }
2584
2585 LOG_DBG("Getting ObjC method's interface with local id {}",
2586 parent_decl->getID());
2587
2588 const auto maybe_method_class = get_participant<model::class_>(parent_decl);
2589
2590 if (!maybe_method_class) {
2591 LOG_DBG("Cannot find parent class_ for method {} in class {}",
2592 declaration->getQualifiedNameAsString(),
2593 declaration->getClassInterface()->getQualifiedNameAsString());
2594 return {};
2595 }
2596
2597 const auto &method_class = maybe_method_class.value();
2598
2599 method_model_ptr->is_void(declaration->getReturnType()->isVoidType());
2600
2601 method_model_ptr->set_class_id(method_class.id());
2602 method_model_ptr->set_class_full_name(method_class.full_name(false));
2603 method_model_ptr->set_name(get_participant(method_model_ptr->class_id())
2604 .value()
2605 .full_name_no_ns() +
2606 "::" + declaration->getNameAsString());
2607
2608 method_model_ptr->return_type(common::to_string(
2609 declaration->getReturnType(), declaration->getASTContext()));
2610
2611 for (const auto *param : declaration->parameters()) {
2612 auto parameter_type =
2613 common::to_string(param->getType(), param->getASTContext());
2615 parameter_type = simplify_system_template(parameter_type);
2616 method_model_ptr->add_parameter(parameter_type);
2617 }
2618
2619 return method_model_ptr;
2620}
2621
2622std::unique_ptr<clanguml::sequence_diagram::model::method>
2623translation_unit_visitor::create_method_model(clang::CXXMethodDecl *declaration)
2624{
2625 auto method_model_ptr = std::make_unique<sequence_diagram::model::method>(
2626 config().using_namespace());
2627
2628 common::model::namespace_ ns{declaration->getQualifiedNameAsString()};
2629 auto method_name = ns.name();
2630 method_model_ptr->set_method_name(method_name);
2631 ns.pop_back();
2632 method_model_ptr->set_name(ns.name());
2633 ns.pop_back();
2634 method_model_ptr->set_namespace(ns);
2635
2636 method_model_ptr->is_defaulted(declaration->isDefaulted());
2637 method_model_ptr->is_assignment(declaration->isCopyAssignmentOperator() ||
2638 declaration->isMoveAssignmentOperator());
2639 method_model_ptr->is_const(declaration->isConst());
2640 method_model_ptr->is_static(declaration->isStatic());
2641 method_model_ptr->is_operator(declaration->isOverloadedOperator());
2642 method_model_ptr->is_constructor(
2643 clang::dyn_cast<clang::CXXConstructorDecl>(declaration) != nullptr);
2644
2645 clang::Decl *parent_decl = declaration->getParent();
2646
2647 if (context().current_class_template_decl_ != nullptr)
2648 parent_decl = context().current_class_template_decl_;
2649
2650 LOG_DBG("Getting method's class with local id {}", parent_decl->getID());
2651
2652 const auto maybe_method_class = get_participant<model::class_>(parent_decl);
2653
2654 if (!maybe_method_class) {
2655 LOG_DBG("Cannot find parent class_ for method {} in class {}",
2656 declaration->getQualifiedNameAsString(),
2657 declaration->getParent()->getQualifiedNameAsString());
2658 return {};
2659 }
2660
2661 const auto &method_class = maybe_method_class.value();
2662
2663 method_model_ptr->is_void(declaration->getReturnType()->isVoidType());
2664
2665 method_model_ptr->set_class_id(method_class.id());
2666 method_model_ptr->set_class_full_name(method_class.full_name(false));
2667 method_model_ptr->set_name(get_participant(method_model_ptr->class_id())
2668 .value()
2669 .full_name_no_ns() +
2670 "::" + declaration->getNameAsString());
2671
2672 method_model_ptr->return_type(common::to_string(
2673 declaration->getReturnType(), declaration->getASTContext()));
2674
2675 for (const auto *param : declaration->parameters()) {
2676 auto parameter_type =
2677 common::to_string(param->getType(), param->getASTContext());
2679 parameter_type = simplify_system_template(parameter_type);
2680 method_model_ptr->add_parameter(config().using_namespace().relative(
2681 simplify_system_template(parameter_type)));
2682 }
2683
2684 return method_model_ptr;
2685}
2686
2687bool translation_unit_visitor::should_include(const clang::TagDecl *decl) const
2688{
2690 dynamic_cast<const clang::NamedDecl *>(decl));
2691}
2692
2694 const clang::ObjCContainerDecl *decl) const
2695{
2697 dynamic_cast<const clang::NamedDecl *>(decl));
2698}
2699
2701 const clang::LambdaExpr *expr) const
2702{
2703 if (context().caller_id() == 0)
2704 return false;
2705
2706 if (!context().valid())
2707 return false;
2708
2709 const auto expr_file = expr->getBeginLoc().printToString(source_manager());
2710
2711 if (!diagram().should_include(
2713 return false;
2714
2715 return true;
2716}
2717
2719 const clang::ObjCMessageExpr *expr) const
2720{
2721 if (context().caller_id() == 0)
2722 return false;
2723
2724 if (!context().valid())
2725 return false;
2726
2727 const auto expr_file = expr->getBeginLoc().printToString(source_manager());
2728
2729 if (!diagram().should_include(
2731 return false;
2732
2733 return true;
2734}
2735
2736bool translation_unit_visitor::should_include(const clang::CallExpr *expr) const
2737{
2738 if (context().caller_id() == 0)
2739 return false;
2740
2741 // Skip casts, moves and such
2742 if (expr->isCallToStdMove())
2743 return false;
2744
2745 if (expr->isImplicitCXXThis())
2746 return false;
2747
2748 if (clang::dyn_cast_or_null<clang::ImplicitCastExpr>(expr) != nullptr)
2749 return false;
2750
2751 if (!context().valid())
2752 return false;
2753
2754 const auto expr_file = expr->getBeginLoc().printToString(source_manager());
2755
2756 if (!diagram().should_include(
2758 return false;
2759
2760 const auto *callee_decl = expr->getCalleeDecl();
2761
2762 if (callee_decl != nullptr) {
2763 const auto *callee_function = callee_decl->getAsFunction();
2764
2765 if ((callee_function == nullptr) || !should_include(callee_function)) {
2766 LOG_DBG("Skipping call expression at {}",
2767 expr->getBeginLoc().printToString(source_manager()));
2768 return false;
2769 }
2770
2771 return should_include(callee_function);
2772 }
2773
2774 return true;
2775}
2776
2778 const clang::CXXMethodDecl *decl) const
2779{
2780 if (!should_include(decl->getParent()))
2781 return false;
2782
2783 if (!diagram().should_include(
2784 common::access_specifier_to_access_t(decl->getAccess())))
2785 return false;
2786
2787 LOG_DBG("Including method {}", decl->getQualifiedNameAsString());
2788
2789 return true;
2790}
2791
2793 const clang::ObjCMethodDecl *decl) const
2794{
2795 if (!diagram().should_include(
2796 common::access_specifier_to_access_t(decl->getAccess())))
2797 return false;
2798
2799 LOG_DBG("Including ObjC method {}", decl->getQualifiedNameAsString());
2800
2801 return true;
2802}
2803
2805 const clang::FunctionDecl *decl) const
2806{
2808}
2809
2811 const clang::FunctionTemplateDecl *decl) const
2812{
2813 return visitor_specialization_t::should_include(decl->getAsFunction());
2814}
2815
2817 const clang::ClassTemplateDecl *decl) const
2818{
2820}
2821
2822std::optional<std::pair<unsigned int, std::string>>
2824 const clang::ASTContext &context, const eid_t caller_id,
2825 const clang::Stmt *stmt)
2826{
2827 const auto *raw_comment =
2829
2830 if (raw_comment == nullptr)
2831 return {};
2832
2833 if (!caller_id.is_global() &&
2835 .emplace(caller_id.ast_local_value(), raw_comment)
2836 .second) {
2837 return {};
2838 }
2839
2840 const auto &[decorators, stripped_comment] = decorators::parse(
2841 raw_comment->getFormattedText(sm, sm.getDiagnostics()));
2842
2843 if (stripped_comment.empty())
2844 return {};
2845
2846 return {{raw_comment->getBeginLoc().getHashValue(), stripped_comment}};
2847}
2848} // namespace clanguml::sequence_diagram::visitor