0.6.2
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 auto context_backup = context();
160
161 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXRecordDecl(
162 declaration);
163
164 call_expression_context_ = context_backup;
165
166 return true;
167}
168
170 clang::CXXRecordDecl *declaration)
171{
172 if (!should_include(declaration))
173 return true;
174
175 // Skip this class if it's parent template is already in the model
176 if (declaration->isTemplated() &&
177 declaration->getDescribedTemplate() != nullptr) {
178 if (get_unique_id(eid_t{declaration->getDescribedTemplate()->getID()}))
179 return true;
180 }
181
182 LOG_TRACE("Visiting class declaration at {}",
183 declaration->getBeginLoc().printToString(source_manager()));
184
185 // Build the class declaration and store it in the diagram, even
186 // if we don't need it for any of the participants of this diagram
187 auto class_model_ptr = create_class_model(declaration);
188
189 if (!class_model_ptr)
190 return true;
191
192 context().reset();
193
194 const auto class_id = class_model_ptr->id();
195
196 set_unique_id(declaration->getID(), class_id);
197
198 auto &class_model =
199 diagram()
200 .get_participant<sequence_diagram::model::class_>(class_id)
201 .has_value()
202 ? *diagram()
203 .get_participant<sequence_diagram::model::class_>(class_id)
204 .get()
205 : *class_model_ptr;
206
207 if (!declaration->isCompleteDefinition()) {
208 forward_declarations_.emplace(class_id, std::move(class_model_ptr));
209 return true;
210 }
211
212 forward_declarations_.erase(class_id);
213
214 if (diagram().should_include(class_model)) {
215 LOG_DBG("Adding class participant {} with id {}",
216 class_model.full_name(false), class_model.id());
217
218 assert(class_model.id() == class_id);
219
220 context().set_caller_id(class_id);
221 context().update(declaration);
222
223 diagram().add_participant(std::move(class_model_ptr));
224 }
225 else {
226 LOG_DBG("Skipping class {} with id {}", class_model.full_name(true),
227 class_id);
228 }
229
230 return true;
231}
232
234 clang::ClassTemplateDecl *declaration)
235{
236 if (!should_include(declaration))
237 return true;
238
239 LOG_TRACE("Visiting class template declaration {} at {} [{}]",
240 declaration->getQualifiedNameAsString(),
241 declaration->getLocation().printToString(source_manager()),
242 (void *)declaration);
243
244 auto class_model_ptr = create_class_model(declaration->getTemplatedDecl());
245
246 if (!class_model_ptr)
247 return true;
248
249 tbuilder().build_from_template_declaration(*class_model_ptr, *declaration);
250
251 const auto class_full_name = class_model_ptr->full_name(false);
252 const auto id = common::to_id(class_full_name);
253
254 // Override the id with the template id, for now we don't care about the
255 // underlying templated class id
256 class_model_ptr->set_id(id);
257
258 set_unique_id(declaration->getID(), id);
259
260 if (!declaration->getTemplatedDecl()->isCompleteDefinition()) {
261 forward_declarations_.emplace(id, std::move(class_model_ptr));
262 return true;
263 }
264 forward_declarations_.erase(id);
265
266 if (diagram().should_include(*class_model_ptr)) {
267 LOG_DBG("Adding class template participant {} with id {}",
268 class_full_name, id);
269
271 context().update(declaration);
272
273 diagram().add_participant(std::move(class_model_ptr));
274 }
275
276 return true;
277}
278
280 clang::ClassTemplateSpecializationDecl *declaration)
281{
282 if (!should_include(declaration))
283 return true;
284
285 LOG_TRACE("Visiting template specialization declaration {} at {}",
286 declaration->getQualifiedNameAsString(),
287 declaration->getLocation().printToString(source_manager()));
288
289 if (declaration->isLocalClass() != nullptr)
290 return true;
291
292 auto template_specialization_ptr =
294
295 if (!template_specialization_ptr)
296 return true;
297
298 const auto class_full_name = template_specialization_ptr->full_name(false);
299 const auto id = common::to_id(class_full_name);
300
301 template_specialization_ptr->set_id(id);
302
303 set_unique_id(declaration->getID(), id);
304
305 if (!declaration->isCompleteDefinition()) {
306 forward_declarations_.emplace(
307 id, std::move(template_specialization_ptr));
308 return true;
309 }
310 forward_declarations_.erase(id);
311
312 if (diagram().should_include(*template_specialization_ptr)) {
313 LOG_DBG(
314 "Adding class template specialization participant {} with id {}",
315 class_full_name, id);
316
318 context().update(declaration);
319
320 diagram().add_participant(std::move(template_specialization_ptr));
321 }
322
323 return true;
324}
325
327 clang::ObjCMethodDecl *declaration)
328{
329 // We need to backup the context, since other methods or functions can
330 // be traversed during this traversal (e.g. template function/method
331 // specializations)
332 auto context_backup = context();
333
334 RecursiveASTVisitor<translation_unit_visitor>::TraverseObjCMethodDecl(
335 declaration);
336
337 call_expression_context_ = context_backup;
338
339 return true;
340}
341
343 clang::ObjCMethodDecl *declaration)
344{
345 if (!should_include(declaration))
346 return true;
347
348 LOG_TRACE("Visiting ObjC method {} in class",
349 declaration->getQualifiedNameAsString());
350
351 context().update(declaration);
352
353 auto method_model_ptr = create_objc_method_model(declaration);
354
355 if (!method_model_ptr)
356 return true;
357
358 process_comment(*declaration, *method_model_ptr);
359
360 set_source_location(*declaration, *method_model_ptr);
361
362 const auto method_full_name = method_model_ptr->full_name(false);
363
364 method_model_ptr->set_id(common::to_id(method_full_name));
365
366 set_unique_id(declaration->getID(), method_model_ptr->id());
367
368 LOG_TRACE("Set id {} --> {} for method name {} [{}]", declaration->getID(),
369 method_model_ptr->id(), method_full_name,
370 declaration->isThisDeclarationADefinition());
371
372 context().update(declaration);
373
374 context().set_caller_id(method_model_ptr->id());
375
376 diagram().add_participant(std::move(method_model_ptr));
377
378 return true;
379}
380
382 clang::CXXMethodDecl *declaration)
383{
384 // We need to backup the context, since other methods or functions can
385 // be traversed during this traversal (e.g. template function/method
386 // specializations)
387 auto context_backup = context();
388
389 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXMethodDecl(
390 declaration);
391
392 call_expression_context_ = context_backup;
393
394 return true;
395}
396
398 clang::CXXMethodDecl *declaration)
399{
400 if (!should_include(declaration))
401 return true;
402
403 if (!declaration->isThisDeclarationADefinition()) {
404 if (auto *declaration_definition = declaration->getDefinition();
405 declaration_definition != nullptr) {
406 if (auto *method_definition = clang::dyn_cast<clang::CXXMethodDecl>(
407 declaration_definition);
408 method_definition != nullptr) {
409 LOG_DBG("Calling VisitCXXMethodDecl recursively for forward "
410 "declaration");
411
412 return VisitCXXMethodDecl(method_definition);
413 }
414 }
415 }
416
417 LOG_TRACE("Visiting method {} in class {} [{}]",
418 declaration->getQualifiedNameAsString(),
419 declaration->getParent()->getQualifiedNameAsString(),
420 (void *)declaration->getParent());
421
422 context().update(declaration);
423
424 auto method_model_ptr = create_method_model(declaration);
425
426 if (!method_model_ptr)
427 return true;
428
429 process_comment(*declaration, *method_model_ptr);
430
431 set_source_location(*declaration, *method_model_ptr);
432
433 const auto method_full_name = method_model_ptr->full_name(false);
434
435 method_model_ptr->set_id(common::to_id(method_full_name));
436
437 // Callee methods in call expressions are referred to by first declaration
438 // id, so they should both be mapped to method_model
439 if (declaration->isThisDeclarationADefinition()) {
441 declaration->getFirstDecl()->getID(), method_model_ptr->id());
442 }
443
444 set_unique_id(declaration->getID(), method_model_ptr->id());
445
446 LOG_TRACE("Set id {} --> {} for method name {} [{}]", declaration->getID(),
447 method_model_ptr->id(), method_full_name,
448 declaration->isThisDeclarationADefinition());
449
450 context().update(declaration);
451
452 context().set_caller_id(method_model_ptr->id());
453
454 diagram().add_participant(std::move(method_model_ptr));
455
456 return true;
457}
458
460 clang::FunctionDecl *declaration)
461{
462 // We need to backup the context, since other methods or functions can
463 // be traversed during this traversal (e.g. template function/method
464 // specializations)
465 auto context_backup = context();
466
467 RecursiveASTVisitor<translation_unit_visitor>::TraverseFunctionDecl(
468 declaration);
469
470 call_expression_context_ = context_backup;
471
472 return true;
473}
474
476 clang::FunctionDecl *declaration)
477{
478 if (declaration->isCXXClassMember())
479 return true;
480
481 if (!should_include(declaration))
482 return true;
483
484 if (!declaration->isThisDeclarationADefinition()) {
485 if (auto *declaration_definition = declaration->getDefinition();
486 declaration_definition != nullptr)
487 return VisitFunctionDecl(
488 static_cast<clang::FunctionDecl *>(declaration_definition));
489 }
490
491 LOG_TRACE("Visiting function declaration {} at {}",
492 declaration->getQualifiedNameAsString(),
493 declaration->getLocation().printToString(source_manager()));
494
495 if (declaration->isTemplated()) {
496 if (declaration->getDescribedTemplate() != nullptr) {
497 // If the described templated of this function is already in the
498 // model skip it:
499 if (get_unique_id(
500 eid_t{declaration->getDescribedTemplate()->getID()}))
501 return true;
502 }
503 }
504
505 std::unique_ptr<model::function> function_model_ptr{};
506
507 if (declaration->isFunctionTemplateSpecialization()) {
508 function_model_ptr =
510 }
511 else {
512 function_model_ptr = build_function_model(*declaration);
513 }
514
515 if (!function_model_ptr)
516 return true;
517
518 function_model_ptr->set_id(
519 common::to_id(function_model_ptr->full_name(false)));
520
521 function_model_ptr->is_void(declaration->getReturnType()->isVoidType());
522
523 function_model_ptr->is_operator(declaration->isOverloadedOperator());
524
525 function_model_ptr->is_cuda_kernel(
526 common::has_attr(declaration, clang::attr::CUDAGlobal));
527
528 function_model_ptr->is_cuda_device(
529 common::has_attr(declaration, clang::attr::CUDADevice));
530
531 function_model_ptr->is_coroutine(common::is_coroutine(*declaration));
532
533 context().update(declaration);
534
535 context().set_caller_id(function_model_ptr->id());
536
537 if (declaration->isThisDeclarationADefinition()) {
539 declaration->getFirstDecl()->getID(), function_model_ptr->id());
540 }
541
542 set_unique_id(declaration->getID(), function_model_ptr->id());
543
544 process_comment(*declaration, *function_model_ptr);
545
546 set_source_location(*declaration, *function_model_ptr);
547
548 diagram().add_participant(std::move(function_model_ptr));
549
550 return true;
551}
552
554 clang::FunctionTemplateDecl *declaration)
555{
556 // We need to backup the context, since other methods or functions can
557 // be traversed during this traversal (e.g. template function/method
558 // specializations)
559 auto context_backup = context();
560
561 RecursiveASTVisitor<translation_unit_visitor>::TraverseFunctionTemplateDecl(
562 declaration);
563
564 call_expression_context_ = context_backup;
565
566 return true;
567}
568
570 clang::FunctionTemplateDecl *declaration)
571{
572 if (!should_include(declaration))
573 return true;
574
575 const auto function_name = declaration->getQualifiedNameAsString();
576
577 LOG_TRACE("Visiting function template declaration {} at {}", function_name,
578 declaration->getLocation().printToString(source_manager()));
579
580 auto function_template_model = build_function_template(*declaration);
581
582 process_comment(*declaration, *function_template_model);
583
584 set_source_location(*declaration, *function_template_model);
585 set_owning_module(*declaration, *function_template_model);
586
587 function_template_model->is_void(
588 declaration->getAsFunction()->getReturnType()->isVoidType());
589
590 function_template_model->set_id(
591 common::to_id(function_template_model->full_name(false)));
592
593 function_template_model->is_operator(
594 declaration->getAsFunction()->isOverloadedOperator());
595
596 context().update(declaration);
597
598 context().set_caller_id(function_template_model->id());
599
600 set_unique_id(declaration->getID(), function_template_model->id());
601
602 diagram().add_participant(std::move(function_template_model));
603
604 return true;
605}
606
607bool translation_unit_visitor::VisitLambdaExpr(clang::LambdaExpr *expr)
608{
609 if (!should_include(expr))
610 return true;
611
612 const auto lambda_full_name =
613 expr->getLambdaClass()->getCanonicalDecl()->getNameAsString();
614
615 LOG_TRACE("Visiting lambda expression {} at {} [caller_id = {}]",
616 lambda_full_name, expr->getBeginLoc().printToString(source_manager()),
617 context().caller_id());
618
619 LOG_TRACE("Lambda call operator ID {} - lambda class ID {}, class call "
620 "operator ID {}",
621 expr->getCallOperator()->getID(), expr->getLambdaClass()->getID(),
622 expr->getLambdaClass()->getLambdaCallOperator()->getID());
623
624 // Create lambda class participant
625 auto *cls = expr->getLambdaClass();
626 auto lambda_class_model_ptr = create_class_model(cls);
627
628 if (!lambda_class_model_ptr)
629 return true;
630
631 lambda_class_model_ptr->is_lambda(true);
632
633 const auto cls_id = lambda_class_model_ptr->id();
634
635 set_unique_id(cls->getID(), cls_id);
636
637 auto lambda_method_model_ptr =
638 create_lambda_method_model(expr->getCallOperator());
639
640 lambda_method_model_ptr->set_class_id(cls_id);
641
642 // If this is a nested lambda, prepend the parent lambda name to this lambda
643 auto lambda_class_full_name = lambda_class_model_ptr->full_name(false);
644 lambda_method_model_ptr->set_class_full_name(lambda_class_full_name);
645
646 diagram().add_participant(std::move(lambda_class_model_ptr));
647
648 lambda_method_model_ptr->set_id(
649 common::to_id(get_participant(cls_id).value().full_name(false) +
650 "::" + lambda_method_model_ptr->full_name_no_ns()));
651
652 get_participant<model::class_>(cls_id).value().set_lambda_operator_id(
653 lambda_method_model_ptr->id());
654
655 // If lambda expression is in an argument to a method/function, and that
656 // method function would be excluded by filters and if it is not a lambda
657 // call itself
658 if (std::holds_alternative<clang::CallExpr *>(
659 context().current_callexpr()) &&
660 (!context().lambda_caller_id().has_value()) &&
662 std::get<clang::CallExpr *>(context().current_callexpr()))) {
665
666 message m{message_t::kCall, context().caller_id()};
667 set_source_location(*expr, m);
668 m.set_from(context().caller_id());
669 m.set_to(lambda_method_model_ptr->id());
670
672
673 diagram().add_active_participant(m.from());
674 diagram().add_active_participant(m.to());
675
676 LOG_DBG("Found call in lambda expression {} from {} [{}] to {} [{}]",
677 m.message_name(), m.from(), m.from(), m.to(), m.to());
678
679 push_message(std::get<clang::CallExpr *>(context().current_callexpr()),
680 std::move(m));
681 }
682
683 context().enter_lambda_expression(lambda_method_model_ptr->id());
684
686 expr->getCallOperator()->getID(), lambda_method_model_ptr->id());
687
688 diagram().add_participant(std::move(lambda_method_model_ptr));
689
690 [[maybe_unused]] const auto is_generic_lambda = expr->isGenericLambda();
691
692 return true;
693}
694
696{
697 auto context_backup = context();
698
699 RecursiveASTVisitor<translation_unit_visitor>::TraverseLambdaExpr(expr);
700
701 // lambda context is entered inside the visitor
703
704 call_expression_context_ = context_backup;
705
706 return true;
707}
708
710 clang::ObjCMessageExpr *expr)
711{
712 if (!config().include_system_headers() &&
713 source_manager().isInSystemHeader(expr->getSourceRange().getBegin()))
714 return true;
715
716 LOG_TRACE("Entering ObjC message expression at {}",
717 expr->getBeginLoc().printToString(source_manager()));
718
719 context().enter_callexpr(expr);
720
721 RecursiveASTVisitor<translation_unit_visitor>::TraverseObjCMessageExpr(
722 expr);
723
724 LOG_TRACE("Leaving ObjC message expression at {}",
725 expr->getBeginLoc().printToString(source_manager()));
726
728
730
731 return true;
732}
733
735{
736 if (!config().include_system_headers() &&
737 source_manager().isInSystemHeader(expr->getSourceRange().getBegin()))
738 return true;
739
740 LOG_TRACE("Entering co_yield expression at {}",
741 expr->getBeginLoc().printToString(source_manager()));
742
743 context().enter_callexpr(expr);
744
745 RecursiveASTVisitor<translation_unit_visitor>::TraverseCoyieldExpr(expr);
746
747 LOG_TRACE("Leaving co_yield expression at {}",
748 expr->getBeginLoc().printToString(source_manager()));
749
751
753
754 return true;
755}
756
758{
759 if (!config().include_system_headers() &&
760 source_manager().isInSystemHeader(expr->getSourceRange().getBegin()))
761 return true;
762
763 LOG_TRACE("Entering co_await expression at {}",
764 expr->getBeginLoc().printToString(source_manager()));
765
766 context().enter_callexpr(expr);
767
768 RecursiveASTVisitor<translation_unit_visitor>::TraverseCoawaitExpr(expr);
769
770 LOG_TRACE("Leaving co_await expression at {}",
771 expr->getBeginLoc().printToString(source_manager()));
772
774
776
777 return true;
778}
779
781{
782 if (!config().include_system_headers() &&
783 source_manager().isInSystemHeader(stmt->getSourceRange().getBegin()))
784 return true;
785
786 LOG_TRACE("Entering co_return statement at {}",
787 stmt->getBeginLoc().printToString(source_manager()));
788
789 context().enter_callexpr(stmt);
790
791 RecursiveASTVisitor<translation_unit_visitor>::TraverseCoreturnStmt(stmt);
792
793 LOG_TRACE("Leaving co_return statement at {}",
794 stmt->getBeginLoc().printToString(source_manager()));
795
797
799
800 return true;
801}
802
804{
805 if (!config().include_system_headers() &&
806 source_manager().isInSystemHeader(expr->getSourceRange().getBegin()))
807 return true;
808
809 LOG_TRACE("Entering call expression at {}",
810 expr->getBeginLoc().printToString(source_manager()));
811
812 context().enter_callexpr(expr);
813
814 RecursiveASTVisitor<translation_unit_visitor>::TraverseCallExpr(expr);
815
816 LOG_TRACE("Leaving call expression at {}",
817 expr->getBeginLoc().printToString(source_manager()));
818
820
822
823 return true;
824}
825
827 clang::CUDAKernelCallExpr *expr)
828{
829 if (!config().include_system_headers() &&
830 source_manager().isInSystemHeader(expr->getSourceRange().getBegin()))
831 return true;
832
833 LOG_TRACE("Entering CUDA kernel call expression at {}",
834 expr->getBeginLoc().printToString(source_manager()));
835
836 context().enter_callexpr(expr);
837
838 RecursiveASTVisitor<translation_unit_visitor>::TraverseCallExpr(expr);
839
840 LOG_TRACE("Leaving CUDA kernel call expression at {}",
841 expr->getBeginLoc().printToString(source_manager()));
842
844
846
847 return true;
848}
849
851 clang::CXXMemberCallExpr *expr)
852{
853 if (!config().include_system_headers() &&
854 source_manager().isInSystemHeader(expr->getSourceRange().getBegin()))
855 return true;
856
857 LOG_TRACE("Entering member call expression at {}",
858 expr->getBeginLoc().printToString(source_manager()));
859
860 context().enter_callexpr(expr);
861
862 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXMemberCallExpr(
863 expr);
864
865 LOG_TRACE("Leaving member call expression at {}",
866 expr->getBeginLoc().printToString(source_manager()));
867
869
871
872 return true;
873}
874
876 clang::CXXOperatorCallExpr *expr)
877{
878 context().enter_callexpr(expr);
879
880 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXOperatorCallExpr(
881 expr);
882
884
886
887 return true;
888}
889
891 clang::CXXTemporaryObjectExpr *expr)
892{
893 context().enter_callexpr(expr);
894
895 RecursiveASTVisitor<
897
899 clang::dyn_cast<clang::CXXConstructExpr>(expr));
900
902
904
905 return true;
906}
907
909 clang::CXXConstructExpr *expr)
910{
911 LOG_TRACE("Entering cxx construct call expression at {}",
912 expr->getBeginLoc().printToString(source_manager()));
913
914 context().enter_callexpr(expr);
915
916 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXConstructExpr(
917 expr);
918
920
921 LOG_TRACE("Leaving cxx construct call expression at {}",
922 expr->getBeginLoc().printToString(source_manager()));
923
925
927
928 return true;
929}
930
932{
933 LOG_TRACE("Entering return statement at {}",
934 stmt->getBeginLoc().printToString(source_manager()));
935
936 context().enter_callexpr(stmt);
937
938 RecursiveASTVisitor<translation_unit_visitor>::TraverseReturnStmt(stmt);
939
940 LOG_TRACE("Leaving return statement at {}",
941 stmt->getBeginLoc().printToString(source_manager()));
942
944
946
947 return true;
948}
949
951{
956
957 if (stmt == nullptr)
958 return true;
959
960 const auto *current_ifstmt = context().current_ifstmt();
961 const auto *current_elseifstmt =
962 current_ifstmt != nullptr ? context().current_elseifstmt() : nullptr;
963
964 //
965 // Add final else block (not else if)
966 //
967 if (current_elseifstmt != nullptr) {
968 if (current_elseifstmt->getElse() == stmt) {
969 const auto current_caller_id = context().caller_id();
970
971 if (current_caller_id.value() != 0) {
972 model::message m{message_t::kElse, current_caller_id};
973 set_source_location(*stmt, m);
974 diagram().add_message(std::move(m));
975 }
976 }
977 }
978 else if (current_ifstmt != nullptr) {
979 if (current_ifstmt->getElse() == stmt) {
980 const auto current_caller_id = context().caller_id();
981
982 if (current_caller_id.value() != 0) {
983 model::message m{message_t::kElse, current_caller_id};
984 set_source_location(*stmt, m);
985 diagram().add_message(std::move(m));
986 }
987 }
988 }
989
990 RecursiveASTVisitor<translation_unit_visitor>::TraverseCompoundStmt(stmt);
991
992 return true;
993}
994
996{
1001
1002 bool elseif_block{false};
1003
1004 const auto current_caller_id = context().caller_id();
1005 const auto *current_ifstmt = context().current_ifstmt();
1006 const auto *current_elseifstmt =
1007 current_ifstmt != nullptr ? context().current_elseifstmt() : nullptr;
1008
1009 std::string condition_text;
1010 if (config().generate_condition_statements())
1011 condition_text = common::get_condition_text(source_manager(), stmt);
1012
1013 // Check if this is a beginning of a new if statement, or an
1014 // else if condition of the current if statement
1015 auto child_stmt_compare = [stmt](auto *child_stmt) {
1016 return child_stmt == stmt;
1017 };
1018
1019 if (current_ifstmt != nullptr)
1020 elseif_block = elseif_block ||
1021 std::any_of(current_ifstmt->children().begin(),
1022 current_ifstmt->children().end(), child_stmt_compare);
1023 if (current_elseifstmt != nullptr)
1024 elseif_block = elseif_block ||
1025 std::any_of(current_elseifstmt->children().begin(),
1026 current_elseifstmt->children().end(), child_stmt_compare);
1027
1028 if ((current_caller_id.value() != 0) && !stmt->isConstexpr()) {
1029 if (elseif_block) {
1030 context().enter_elseifstmt(stmt);
1031
1032 message m{message_t::kElseIf, current_caller_id};
1033 set_source_location(*stmt, m);
1034 m.condition_text(condition_text);
1035 m.set_comment(get_expression_comment(source_manager(),
1036 *context().get_ast_context(), current_caller_id, stmt));
1037 diagram().add_block_message(std::move(m));
1038 }
1039 else {
1040 context().enter_ifstmt(stmt);
1041 LOG_TRACE("Entered if statement at {}",
1042 stmt->getBeginLoc().printToString(source_manager()));
1043
1044 message m{message_t::kIf, current_caller_id};
1045 set_source_location(*stmt, m);
1046 m.condition_text(condition_text);
1047 m.set_comment(get_expression_comment(source_manager(),
1048 *context().get_ast_context(), current_caller_id, stmt));
1049 diagram().add_block_message(std::move(m));
1050 }
1051 }
1052
1053 RecursiveASTVisitor<translation_unit_visitor>::TraverseIfStmt(stmt);
1054
1055 if ((current_caller_id.value() != 0) && !stmt->isConstexpr()) {
1056 if (!elseif_block) {
1057 diagram().end_block_message(
1058 {message_t::kIfEnd, current_caller_id}, message_t::kIf);
1060 }
1061 }
1062
1063 return true;
1064}
1065
1067{
1071
1072 const auto current_caller_id = context().caller_id();
1073
1074 std::string condition_text;
1075 if (config().generate_condition_statements())
1076 condition_text = common::get_condition_text(source_manager(), stmt);
1077
1078 if (current_caller_id.value() != 0) {
1079 LOG_TRACE("Entering while statement at {}",
1080 stmt->getBeginLoc().printToString(source_manager()));
1081
1082 context().enter_loopstmt(stmt);
1083 message m{message_t::kWhile, current_caller_id};
1084 set_source_location(*stmt, m);
1085 m.condition_text(condition_text);
1086 m.set_comment(get_expression_comment(source_manager(),
1087 *context().get_ast_context(), current_caller_id, stmt));
1088 diagram().add_block_message(std::move(m));
1089 }
1090
1091 RecursiveASTVisitor<translation_unit_visitor>::TraverseWhileStmt(stmt);
1092
1093 if (current_caller_id.value() != 0) {
1094 diagram().end_block_message(
1095 {message_t::kWhileEnd, current_caller_id}, message_t::kWhile);
1097 }
1098
1099 return true;
1100}
1101
1103{
1107
1108 const auto current_caller_id = context().caller_id();
1109
1110 std::string condition_text;
1111 if (config().generate_condition_statements())
1112 condition_text = common::get_condition_text(source_manager(), stmt);
1113
1114 if (current_caller_id.value() != 0) {
1115 context().enter_loopstmt(stmt);
1116 message m{message_t::kDo, current_caller_id};
1117 set_source_location(*stmt, m);
1118 m.condition_text(condition_text);
1119 m.set_comment(get_expression_comment(source_manager(),
1120 *context().get_ast_context(), current_caller_id, stmt));
1121 diagram().add_block_message(std::move(m));
1122 }
1123
1124 RecursiveASTVisitor<translation_unit_visitor>::TraverseDoStmt(stmt);
1125
1126 if (current_caller_id.value() != 0) {
1127 diagram().end_block_message(
1128 {message_t::kDoEnd, current_caller_id}, message_t::kDo);
1130 }
1131
1132 return true;
1133}
1134
1136{
1140
1141 const auto current_caller_id = context().caller_id();
1142
1143 std::string condition_text;
1144 if (config().generate_condition_statements())
1145 condition_text = common::get_condition_text(source_manager(), stmt);
1146
1147 if (current_caller_id.value() != 0) {
1148 context().enter_loopstmt(stmt);
1149 message m{message_t::kFor, current_caller_id};
1150 set_source_location(*stmt, m);
1151 m.condition_text(condition_text);
1152
1153 m.set_comment(get_expression_comment(source_manager(),
1154 *context().get_ast_context(), current_caller_id, stmt));
1155
1156 diagram().add_block_message(std::move(m));
1157 }
1158
1159 RecursiveASTVisitor<translation_unit_visitor>::TraverseForStmt(stmt);
1160
1161 if (current_caller_id.value() != 0) {
1162 diagram().end_block_message(
1163 {message_t::kForEnd, current_caller_id}, message_t::kFor);
1165 }
1166
1167 return true;
1168}
1169
1171{
1175
1176 const auto current_caller_id = context().caller_id();
1177
1178 if (current_caller_id.value() != 0) {
1179 context().enter_trystmt(stmt);
1180 message m{message_t::kTry, current_caller_id};
1181 set_source_location(*stmt, m);
1182 m.set_comment(get_expression_comment(source_manager(),
1183 *context().get_ast_context(), current_caller_id, stmt));
1184 diagram().add_block_message(std::move(m));
1185 }
1186
1187 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXTryStmt(stmt);
1188
1189 if (current_caller_id.value() != 0) {
1190 diagram().end_block_message(
1191 {message_t::kTryEnd, current_caller_id}, message_t::kTry);
1193 }
1194
1195 return true;
1196}
1197
1199{
1203
1204 const auto current_caller_id = context().caller_id();
1205
1206 if ((current_caller_id.value() != 0) &&
1207 (context().current_trystmt() != nullptr)) {
1208 std::string caught_type;
1209 if (stmt->getCaughtType().isNull())
1210 caught_type = "...";
1211 else
1212 caught_type = common::to_string(
1213 stmt->getCaughtType(), *context().get_ast_context());
1214
1215 model::message m{message_t::kCatch, current_caller_id};
1216 m.set_message_name(std::move(caught_type));
1217 diagram().add_message(std::move(m));
1218 }
1219
1220 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXCatchStmt(stmt);
1221
1222 return true;
1223}
1224
1226 clang::CXXForRangeStmt *stmt)
1227{
1231
1232 const auto current_caller_id = context().caller_id();
1233
1234 std::string condition_text;
1235 if (config().generate_condition_statements())
1236 condition_text = common::get_condition_text(source_manager(), stmt);
1237
1238 if (current_caller_id.value() != 0) {
1239 context().enter_loopstmt(stmt);
1240 message m{message_t::kFor, current_caller_id};
1241 set_source_location(*stmt, m);
1242 m.condition_text(condition_text);
1243 m.set_comment(get_expression_comment(source_manager(),
1244 *context().get_ast_context(), current_caller_id, stmt));
1245 diagram().add_block_message(std::move(m));
1246 }
1247
1248 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXForRangeStmt(
1249 stmt);
1250
1251 if (current_caller_id.value() != 0) {
1252 diagram().end_block_message(
1253 {message_t::kForEnd, current_caller_id}, message_t::kFor);
1255 }
1256
1257 return true;
1258}
1259
1261{
1263
1264 const auto current_caller_id = context().caller_id();
1265
1266 if (current_caller_id.value() != 0) {
1267 context().enter_switchstmt(stmt);
1268 model::message m{message_t::kSwitch, current_caller_id};
1269 set_source_location(*stmt, m);
1270 m.set_comment(get_expression_comment(source_manager(),
1271 *context().get_ast_context(), current_caller_id, stmt));
1272 diagram().add_block_message(std::move(m));
1273 }
1274
1275 RecursiveASTVisitor<translation_unit_visitor>::TraverseSwitchStmt(stmt);
1276
1277 if (current_caller_id.value() != 0) {
1279 diagram().end_block_message(
1280 {message_t::kSwitchEnd, current_caller_id}, message_t::kSwitch);
1281 }
1282
1283 return true;
1284}
1285
1287{
1289
1290 const auto current_caller_id = context().caller_id();
1291
1292 if ((current_caller_id.value() != 0) &&
1293 (context().current_switchstmt() != nullptr)) {
1294 model::message m{message_t::kCase, current_caller_id};
1295 m.set_message_name(common::to_string(stmt->getLHS()));
1296 diagram().add_case_stmt_message(std::move(m));
1297 }
1298
1299 RecursiveASTVisitor<translation_unit_visitor>::TraverseCaseStmt(stmt);
1300
1301 return true;
1302}
1303
1305{
1307
1308 const auto current_caller_id = context().caller_id();
1309
1310 if ((current_caller_id.value() != 0) &&
1311 (context().current_switchstmt() != nullptr)) {
1312 model::message m{message_t::kCase, current_caller_id};
1313 m.set_message_name("default");
1314 diagram().add_case_stmt_message(std::move(m));
1315 }
1316
1317 RecursiveASTVisitor<translation_unit_visitor>::TraverseDefaultStmt(stmt);
1318
1319 return true;
1320}
1321
1323 clang::ConditionalOperator *stmt)
1324{
1326
1327 const auto current_caller_id = context().caller_id();
1328
1329 std::string condition_text;
1330 if (config().generate_condition_statements())
1331 condition_text = common::get_condition_text(source_manager(), stmt);
1332
1333 if (current_caller_id.value() != 0) {
1335 model::message m{message_t::kConditional, current_caller_id};
1336 set_source_location(*stmt, m);
1337 m.condition_text(condition_text);
1338 m.set_comment(get_expression_comment(source_manager(),
1339 *context().get_ast_context(), current_caller_id, stmt));
1340 diagram().add_block_message(std::move(m));
1341 }
1342
1343 RecursiveASTVisitor<translation_unit_visitor>::TraverseStmt(
1344 stmt->getCond());
1345
1346 RecursiveASTVisitor<translation_unit_visitor>::TraverseStmt(
1347 stmt->getTrueExpr());
1348
1349 if (current_caller_id.value() != 0) {
1350 model::message m{message_t::kConditionalElse, current_caller_id};
1351 set_source_location(*stmt, m);
1352 diagram().add_message(std::move(m));
1353 }
1354
1355 RecursiveASTVisitor<translation_unit_visitor>::TraverseStmt(
1356 stmt->getFalseExpr());
1357
1358 if (current_caller_id.value() != 0) {
1360 diagram().end_block_message(
1361 {message_t::kConditionalEnd, current_caller_id},
1362 message_t::kConditional);
1363 }
1364
1365 return true;
1366}
1367
1369 clang::ObjCMessageExpr *expr)
1370{
1376
1377 if (!context().valid() || context().get_ast_context() == nullptr)
1378 return true;
1379
1380 LOG_TRACE("Visiting ObjC message expression at {} [caller_id = {}]",
1381 expr->getBeginLoc().printToString(source_manager()),
1382 context().caller_id());
1383
1384 message m{message_t::kCall, context().caller_id()};
1385
1386 set_source_location(*expr, m);
1387
1388 const auto *raw_expr_comment = clanguml::common::get_expression_raw_comment(
1389 source_manager(), *context().get_ast_context(), expr);
1390 const auto stripped_comment = process_comment(
1391 raw_expr_comment, context().get_ast_context()->getDiagnostics(), m);
1392
1393 if (m.skip())
1394 return true;
1395
1396 auto generated_message_from_comment = generate_message_from_comment(m);
1397
1398 if (!generated_message_from_comment && !should_include(expr)) {
1399 LOG_DBG("Skipping call expression due to filter at: {}",
1400 expr->getBeginLoc().printToString(source_manager()));
1401
1402 processed_comments().erase(raw_expr_comment);
1403 return true;
1404 }
1405
1406 if (context().is_expr_in_current_control_statement_condition(expr)) {
1407 m.set_message_scope(common::model::message_scope_t::kCondition);
1408 }
1409
1410 if (generated_message_from_comment) {
1411 LOG_DBG(
1412 "Message for this call expression is taken from comment directive");
1413 return true;
1414 }
1415
1417
1418 // Add message to diagram
1419 if (m.from().value() > 0 && m.to().value() > 0) {
1420 if (raw_expr_comment != nullptr)
1421 m.set_comment(raw_expr_comment->getBeginLoc().getHashValue(),
1422 stripped_comment);
1423
1425
1426 diagram().add_active_participant(m.from());
1427 diagram().add_active_participant(m.to());
1428
1429 LOG_DBG("Found ObjC message {} from {} [{}] to {} [{}] ",
1430 m.message_name(), m.from(), m.from(), m.to(), m.to());
1431
1432 push_message(expr, std::move(m));
1433 }
1434
1435 return true;
1436}
1437
1439 clang::ObjCPropertyRefExpr *expr)
1440{
1446
1447 if (!context().valid() || context().get_ast_context() == nullptr)
1448 return true;
1449
1450 LOG_TRACE("Visiting ObjC property ref expression at {} [caller_id = {}]",
1451 expr->getBeginLoc().printToString(source_manager()),
1452 context().caller_id());
1453
1454 return true;
1455}
1456
1458{
1464
1465 if (!context().valid() || context().get_ast_context() == nullptr)
1466 return true;
1467
1468 LOG_TRACE("Visiting call expression at {} [caller_id = {}]",
1469 expr->getBeginLoc().printToString(source_manager()),
1470 context().caller_id());
1471
1472 message m{message_t::kCall, context().caller_id()};
1473
1474 m.in_static_declaration_context(within_static_variable_declaration_ > 0);
1475
1476 set_source_location(*expr, m);
1477
1478 const auto *raw_expr_comment = clanguml::common::get_expression_raw_comment(
1479 source_manager(), *context().get_ast_context(), expr);
1480 const auto stripped_comment = process_comment(
1481 raw_expr_comment, context().get_ast_context()->getDiagnostics(), m);
1482
1483 if (m.skip())
1484 return true;
1485
1486 auto generated_message_from_comment = generate_message_from_comment(m);
1487
1488 if (!generated_message_from_comment && !should_include(expr)) {
1489 LOG_DBG("Skipping call expression due to filter at: {}",
1490 expr->getBeginLoc().printToString(source_manager()));
1491
1492 processed_comments().erase(raw_expr_comment);
1493 return true;
1494 }
1495
1496 if (context().is_expr_in_current_control_statement_condition(expr)) {
1497 m.set_message_scope(common::model::message_scope_t::kCondition);
1498 }
1499
1500 auto result = process_callee(expr, m, generated_message_from_comment);
1501
1502 if (!result)
1503 return true;
1504
1505 // Add message to diagram
1506 if (m.from().value() > 0 && m.to().value() > 0) {
1507 if (raw_expr_comment != nullptr)
1508 m.set_comment(raw_expr_comment->getBeginLoc().getHashValue(),
1509 stripped_comment);
1510
1512
1513 diagram().add_active_participant(m.from());
1514 diagram().add_active_participant(m.to());
1515
1516 LOG_DBG("Found call {} from {} [{}] to {} [{}] ", m.message_name(),
1517 m.from(), m.from(), m.to(), m.to());
1518
1519 push_message(expr, std::move(m));
1520 }
1521
1522 return true;
1523}
1524
1526 model::message &m, bool generated_message_from_comment)
1527{
1528 bool result = true;
1529
1530 if (expr == nullptr)
1531 return false;
1532
1533 if (generated_message_from_comment) {
1534 LOG_DBG(
1535 "Message for this call expression is taken from comment directive");
1536 }
1537 //
1538 // Call to a CUDA kernel function
1539 //
1540 else if (const auto *cuda_call_expr =
1541 llvm::dyn_cast_or_null<clang::CUDAKernelCallExpr>(expr);
1542 cuda_call_expr != nullptr) {
1543 result = process_cuda_kernel_call_expression(m, cuda_call_expr);
1544 }
1545 //
1546 // Call to an overloaded operator
1547 //
1548 else if (const auto *operator_call_expr =
1549 llvm::dyn_cast_or_null<clang::CXXOperatorCallExpr>(expr);
1550 operator_call_expr != nullptr) {
1551
1552 result = process_operator_call_expression(m, operator_call_expr);
1553 }
1554 //
1555 // Call to a class method
1556 //
1557 else if (const auto *method_call_expr =
1558 llvm::dyn_cast_or_null<clang::CXXMemberCallExpr>(expr);
1559 method_call_expr != nullptr) {
1560
1561 result = process_class_method_call_expression(m, method_call_expr);
1562 }
1563 //
1564 // Call to function or template
1565 //
1566 else {
1567 auto *callee_decl = expr->getCalleeDecl();
1568
1569 if (callee_decl == nullptr) {
1570 LOG_DBG("Cannot get callee declaration - trying direct function "
1571 "callee...");
1572
1573 callee_decl = expr->getDirectCallee();
1574
1575 if (callee_decl != nullptr)
1576 LOG_DBG("Found function/method callee in: {}",
1577 common::to_string(expr));
1578 }
1579
1580 if (callee_decl == nullptr) {
1581 //
1582 // Call to a method of a class template
1583 //
1584 if (llvm::dyn_cast_or_null<clang::CXXDependentScopeMemberExpr>(
1585 expr->getCallee()) != nullptr) {
1587 }
1588 //
1589 // Unresolved lookup expression are sometimes calls to template
1590 // functions
1591 //
1592 else if (llvm::dyn_cast_or_null<clang::UnresolvedLookupExpr>(
1593 expr->getCallee()) != nullptr) {
1595 }
1596 else if (common::is_lambda_call(expr)) {
1597 LOG_DBG("Processing lambda expression callee");
1598 result = process_lambda_call_expression(m, expr);
1599 }
1600 else if (llvm::dyn_cast_or_null<clang::DependentScopeDeclRefExpr>(
1601 expr->getCallee()) != nullptr) {
1602 LOG_DBG("Processing dependent scope declaration expression "
1603 "callee - not able to infer the template parameter "
1604 "type at this point: {}",
1605 expr->getBeginLoc().printToString(source_manager()));
1606 }
1607 else {
1608 LOG_DBG("Found unsupported callee decl type for: {} at {}",
1609 common::to_string(expr),
1610 expr->getBeginLoc().printToString(source_manager()));
1611 }
1612 }
1613 else {
1614 auto success = process_function_call_expression(m, expr);
1615
1616 if (!success) {
1617 LOG_DBG("Skipping call expression at: {}",
1618 expr->getBeginLoc().printToString(source_manager()));
1619
1620 result = false;
1621 }
1622 }
1623 }
1624
1625 return result;
1626}
1627
1629{
1635
1636 if (!context().valid() || context().get_ast_context() == nullptr)
1637 return true;
1638
1639 LOG_TRACE("Visiting co_await expression at {} [caller_id = {}]",
1640 expr->getBeginLoc().printToString(source_manager()),
1641 context().caller_id());
1642
1643 message m{message_t::kCoAwait, context().caller_id()};
1644 set_source_location(*expr, m);
1645
1646 if (expr->getOperand() != nullptr) {
1647 std::string message_name = common::to_string(expr->getOperand());
1648 m.set_message_name(util::condense_whitespace(message_name));
1649 }
1650
1651 const auto *raw_expr_comment = clanguml::common::get_expression_raw_comment(
1652 source_manager(), *context().get_ast_context(), expr);
1653 const auto stripped_comment = process_comment(
1654 raw_expr_comment, context().get_ast_context()->getDiagnostics(), m);
1655
1656 if (m.skip())
1657 return true;
1658
1659 auto generated_message_from_comment = generate_message_from_comment(m);
1660
1661 if (!generated_message_from_comment &&
1663 clang::dyn_cast_or_null<clang::CallExpr>(expr->getResumeExpr()))) {
1664 LOG_DBG("Skipping call expression due to filter at: {}",
1665 expr->getBeginLoc().printToString(source_manager()));
1666
1667 processed_comments().erase(raw_expr_comment);
1668 return true;
1669 }
1670
1671 if (context().is_expr_in_current_control_statement_condition(expr)) {
1672 m.set_message_scope(common::model::message_scope_t::kCondition);
1673 }
1674
1675 auto result = process_callee(
1676 clang::dyn_cast_or_null<clang::CallExpr>(expr->getResumeExpr()), m,
1677 generated_message_from_comment);
1678
1679 if (!result)
1680 return true;
1681
1682 // We can skip the ID of the return activity here, we'll just add it during
1683 // diagram generation
1684 if (m.from().value() > 0) {
1685 if (raw_expr_comment != nullptr)
1686 m.set_comment(raw_expr_comment->getBeginLoc().getHashValue(),
1687 stripped_comment);
1688
1690
1691 diagram().add_active_participant(m.from());
1692
1693 LOG_DBG("Found return call {} from {} [{}] to {} [{}] ",
1694 m.message_name(), m.from(), m.from(), m.to(), m.to());
1695
1696 push_message(expr, std::move(m));
1697 }
1698
1699 return true;
1700}
1701
1703{
1709
1710 if (!context().valid() || context().get_ast_context() == nullptr)
1711 return true;
1712
1713 LOG_TRACE("Visiting return statement at {}",
1714 stmt->getBeginLoc().printToString(source_manager()));
1715
1716 message m{message_t::kReturn, context().caller_id()};
1717
1718 set_source_location(*stmt, m);
1719
1720 const auto *raw_expr_comment = clanguml::common::get_expression_raw_comment(
1721 source_manager(), *context().get_ast_context(), stmt);
1722 const auto stripped_comment = process_comment(
1723 raw_expr_comment, context().get_ast_context()->getDiagnostics(), m);
1724
1725 if (m.skip())
1726 return true;
1727
1728 if (stmt->getRetValue() != nullptr) {
1729 std::string message_name = common::to_string(stmt->getRetValue());
1730 m.set_message_name(util::condense_whitespace(message_name));
1731 }
1732
1733 if (context().lambda_caller_id().has_value() &&
1734 !context().is_local_class()) {
1735 const auto &lambda_model = get_participant<model::method>(
1736 *context().lambda_caller_id()); // NOLINT
1737
1738 if (lambda_model.has_value()) {
1739 if (lambda_model.has_value())
1740 m.set_return_type(lambda_model.value().return_type());
1741 }
1742 }
1743 else if (context().current_function_decl_ != nullptr) {
1744 m.set_return_type(
1745 context().current_function_decl_->getReturnType().getAsString());
1746 }
1747 else if (context().current_function_template_decl_ != nullptr) {
1748 m.set_return_type(context()
1749 .current_function_template_decl_->getAsFunction()
1750 ->getReturnType()
1751 .getAsString());
1752 }
1753 else if (context().current_method_decl_ != nullptr) {
1754 m.set_return_type(
1755 context().current_method_decl_->getReturnType().getAsString());
1756 }
1757 else if (context().current_objc_method_decl_ != nullptr) {
1758 m.set_return_type(
1759 context().current_objc_method_decl_->getReturnType().getAsString());
1760 }
1761
1762 // We can skip the ID of the return activity here, we'll just add it during
1763 // diagram generation
1764 if (m.from().value() > 0) {
1765 if (raw_expr_comment != nullptr)
1766 m.set_comment(raw_expr_comment->getBeginLoc().getHashValue(),
1767 stripped_comment);
1768
1770
1771 diagram().add_active_participant(m.from());
1772
1773 LOG_DBG("Found return call {} from {} [{}] to {} [{}] ",
1774 m.message_name(), m.from(), m.from(), m.to(), m.to());
1775
1776 push_message(stmt, std::move(m));
1777 }
1778
1779 return true;
1780}
1781
1783{
1789
1790 if (!context().valid() || context().get_ast_context() == nullptr)
1791 return true;
1792
1793 LOG_TRACE("Visiting co_yield expression at {}",
1794 expr->getBeginLoc().printToString(source_manager()));
1795
1796 message m{message_t::kCoYield, context().caller_id()};
1797
1798 set_source_location(*expr, m);
1799
1800 const auto *raw_expr_comment = clanguml::common::get_expression_raw_comment(
1801 source_manager(), *context().get_ast_context(), expr);
1802 const auto stripped_comment = process_comment(
1803 raw_expr_comment, context().get_ast_context()->getDiagnostics(), m);
1804
1805 if (m.skip())
1806 return true;
1807
1808 if (expr->getOperand() != nullptr) {
1809 std::string message_name = common::to_string(expr->getOperand());
1810 m.set_message_name(util::condense_whitespace(message_name));
1811 }
1812
1813 if (context().current_function_decl_ != nullptr) {
1814 m.set_return_type(
1815 context().current_function_decl_->getReturnType().getAsString());
1816 }
1817 else if (context().current_function_template_decl_ != nullptr) {
1818 m.set_return_type(context()
1819 .current_function_template_decl_->getAsFunction()
1820 ->getReturnType()
1821 .getAsString());
1822 }
1823 else if (context().current_method_decl_ != nullptr) {
1824 m.set_return_type(
1825 context().current_method_decl_->getReturnType().getAsString());
1826 }
1827 else if (context().current_objc_method_decl_ != nullptr) {
1828 m.set_return_type(
1829 context().current_objc_method_decl_->getReturnType().getAsString());
1830 }
1831
1832 // We can skip the ID of the return activity here, we'll just add it during
1833 // diagram generation
1834 if (m.from().value() > 0) {
1835 if (raw_expr_comment != nullptr)
1836 m.set_comment(raw_expr_comment->getBeginLoc().getHashValue(),
1837 stripped_comment);
1838
1840
1841 diagram().add_active_participant(m.from());
1842
1843 LOG_DBG("Found co_yield call {} from {} [{}] to {} [{}] ",
1844 m.message_name(), m.from(), m.from(), m.to(), m.to());
1845
1846 push_message(expr, std::move(m));
1847 }
1848
1849 return true;
1850}
1851
1852bool translation_unit_visitor::VisitCoreturnStmt(clang::CoreturnStmt *stmt)
1853{
1859
1860 if (!context().valid() || context().get_ast_context() == nullptr)
1861 return true;
1862
1863 LOG_TRACE("Visiting co_return statement at {}",
1864 stmt->getBeginLoc().printToString(source_manager()));
1865
1866 message m{message_t::kCoReturn, context().caller_id()};
1867
1868 set_source_location(*stmt, m);
1869
1870 const auto *raw_expr_comment = clanguml::common::get_expression_raw_comment(
1871 source_manager(), *context().get_ast_context(), stmt);
1872 const auto stripped_comment = process_comment(
1873 raw_expr_comment, context().get_ast_context()->getDiagnostics(), m);
1874
1875 if (m.skip())
1876 return true;
1877
1878 if (stmt->getOperand() != nullptr) {
1879 std::string message_name = common::to_string(stmt->getOperand());
1880 m.set_message_name(util::condense_whitespace(message_name));
1881 }
1882
1883 if (context().current_function_decl_ != nullptr) {
1884 m.set_return_type(
1885 context().current_function_decl_->getReturnType().getAsString());
1886 }
1887 else if (context().current_function_template_decl_ != nullptr) {
1888 m.set_return_type(context()
1889 .current_function_template_decl_->getAsFunction()
1890 ->getReturnType()
1891 .getAsString());
1892 }
1893 else if (context().current_method_decl_ != nullptr) {
1894 m.set_return_type(
1895 context().current_method_decl_->getReturnType().getAsString());
1896 }
1897 else if (context().current_objc_method_decl_ != nullptr) {
1898 m.set_return_type(
1899 context().current_objc_method_decl_->getReturnType().getAsString());
1900 }
1901
1902 // We can skip the ID of the return activity here, we'll just add it during
1903 // diagram generation
1904 if (m.from().value() > 0) {
1905 if (raw_expr_comment != nullptr)
1906 m.set_comment(raw_expr_comment->getBeginLoc().getHashValue(),
1907 stripped_comment);
1908
1910
1911 diagram().add_active_participant(m.from());
1912
1913 LOG_DBG("Found co_return call {} from {} [{}] to {} [{}] ",
1914 m.message_name(), m.from(), m.from(), m.to(), m.to());
1915
1916 push_message(stmt, std::move(m));
1917 }
1918
1919 return true;
1920}
1921
1923{
1924 if (diagram().sequences().count(m.from()) == 0) {
1925 model::activity a{m.from()};
1926 diagram().sequences().insert({m.from(), std::move(a)});
1927 }
1928
1929 // Maintain reverse graph of activity callers
1930 activity_callers_[m.to()].emplace(m.from());
1931}
1932
1934 model::message &m) const
1935{
1936 auto generated_message_from_comment{false};
1937 for (const auto &decorator : m.decorators()) {
1938 auto call_decorator =
1939 std::dynamic_pointer_cast<decorators::call>(decorator);
1940 if (call_decorator &&
1941 call_decorator->applies_to_diagram(config().name)) {
1942 m.set_to(common::to_id(call_decorator->callee));
1943 generated_message_from_comment = true;
1944 break;
1945 }
1946 }
1947 return generated_message_from_comment;
1948}
1949
1951{
1952 if (decl->isStaticLocal())
1954
1955 RecursiveASTVisitor::TraverseVarDecl(decl);
1956
1957 if (decl->isStaticLocal())
1959
1960 return true;
1961}
1962
1964 clang::CXXConstructExpr *expr)
1965{
1971
1972 if (expr == nullptr)
1973 return true;
1974
1975 if (const auto *ctor = expr->getConstructor();
1976 ctor != nullptr && !should_include(ctor))
1977 return true;
1978
1979 LOG_TRACE("Visiting cxx construct expression at {} [caller_id = {}]",
1980 expr->getBeginLoc().printToString(source_manager()),
1981 context().caller_id());
1982
1983 message m{message_t::kCall, context().caller_id()};
1984
1985 m.in_static_declaration_context(within_static_variable_declaration_ > 0);
1986
1987 set_source_location(*expr, m);
1988
1989 if (context().is_expr_in_current_control_statement_condition(expr)) {
1990 m.set_message_scope(common::model::message_scope_t::kCondition);
1991 }
1992
1993 if (!process_construct_expression(m, expr))
1994 return true;
1995
1996 if (m.from().value() > 0 && m.to().value() > 0) {
1998
1999 diagram().add_active_participant(m.from());
2000 diagram().add_active_participant(m.to());
2001
2002 LOG_DBG("Found constructor call {} from {} [{}] to {} [{}] ",
2003 m.message_name(), m.from(), m.from(), m.to(), m.to());
2004
2005 push_message(expr, std::move(m));
2006 }
2007
2008 return true;
2009}
2010
2012 model::message &m, const clang::CUDAKernelCallExpr *expr)
2013{
2014 const auto *callee_decl = expr->getCalleeDecl();
2015
2016 if (callee_decl == nullptr)
2017 return false;
2018
2019 const auto *callee_function = callee_decl->getAsFunction();
2020
2021 if (callee_function == nullptr)
2022 return false;
2023
2024 if (!should_include(callee_function))
2025 return false;
2026
2027 // Skip free functions declared in files outside of included paths
2028 if (config().combine_free_functions_into_file_participants() &&
2030 return false;
2031
2032 auto callee_name = callee_function->getQualifiedNameAsString() + "()";
2033
2034 m.set_to(id_mapper().resolve_or(eid_t{callee_function->getID()}));
2035 m.set_message_name(callee_name.substr(0, callee_name.size() - 2));
2036
2037 return true;
2038}
2039
2041 model::message &m, const clang::CXXOperatorCallExpr *operator_call_expr)
2042{
2043 if (operator_call_expr->getCalleeDecl() == nullptr)
2044 return false;
2045
2046 LOG_DBG("Operator '{}' call expression to {} at {}",
2047 getOperatorSpelling(operator_call_expr->getOperator()),
2048 operator_call_expr->getCalleeDecl()->getID(),
2049 operator_call_expr->getBeginLoc().printToString(source_manager()));
2050
2051 // Handle the case if the callee is a lambda
2052 if (const auto *lambda_method = clang::dyn_cast<clang::CXXMethodDecl>(
2053 operator_call_expr->getCalleeDecl());
2054 lambda_method != nullptr && lambda_method->getParent()->isLambda()) {
2055
2056 LOG_DBG("Operator callee is a lambda: {}",
2057 common::to_string(lambda_method));
2058
2059 const auto source_location{
2060 lambda_source_location(lambda_method->getParent()->getLocation())};
2061
2062 auto lambda_name = make_lambda_name(lambda_method->getParent());
2063
2064 m.set_to(eid_t{lambda_method->getParent()->getID()});
2065 }
2066 else {
2067 const auto operator_ast_id =
2068 operator_call_expr->getCalleeDecl()->getID();
2069 m.set_to(id_mapper().resolve_or(eid_t{operator_ast_id}));
2070 }
2071
2072 m.set_message_name(fmt::format(
2073 "operator{}", getOperatorSpelling(operator_call_expr->getOperator())));
2074
2075 return true;
2076}
2077
2079 model::message &m, const clang::CXXConstructExpr *construct_expr)
2080{
2081 const auto *constructor = construct_expr->getConstructor();
2082 if (constructor == nullptr)
2083 return false;
2084
2085 const auto *constructor_parent = constructor->getParent();
2086 if (constructor_parent == nullptr)
2087 return false;
2088
2089 LOG_DBG("Constructor '{}' call expression to {} at {}",
2090 construct_expr->getConstructor()->getNameAsString(),
2091 constructor->getID(),
2092 construct_expr->getBeginLoc().printToString(source_manager()));
2093
2094 m.set_to(id_mapper().resolve_or(eid_t{constructor->getID()}));
2096 fmt::format("{}::{}", constructor_parent->getQualifiedNameAsString(),
2097 constructor_parent->getNameAsString()));
2098
2099 diagram().add_active_participant(eid_t{constructor->getID()});
2100
2101 return true;
2102}
2103
2105 model::message &m, const clang::ObjCMessageExpr *message_expr)
2106{
2107 const auto *method_decl = message_expr->getMethodDecl();
2108
2109 if (method_decl == nullptr)
2110 return false;
2111
2112 std::string method_name = method_decl->getQualifiedNameAsString();
2113
2114 if (message_expr->getReceiverInterface() == nullptr)
2115 return false;
2116
2117 const auto *callee_decl = message_expr->getReceiverInterface();
2118
2119 if (callee_decl == nullptr)
2120 return false;
2121
2122 if (!should_include(callee_decl) || !should_include(method_decl))
2123 return false;
2124
2125 if (callee_decl->getImplementation() != nullptr &&
2126 callee_decl->getImplementation()->getMethod(method_decl->getSelector(),
2127 method_decl->isInstanceMethod(), true) != nullptr) {
2128 const auto *impl_method_decl =
2129 callee_decl->getImplementation()->getMethod(
2130 method_decl->getSelector(), method_decl->isInstanceMethod(),
2131 true);
2132 m.set_to(eid_t{impl_method_decl->getID()});
2133 }
2134 else {
2135 m.set_to(eid_t{method_decl->getID()});
2136 }
2137
2138 m.set_message_name(method_decl->getNameAsString());
2140 message_expr->getCallReturnType(*context().get_ast_context())
2141 .getAsString());
2142
2143 LOG_TRACE("Set callee ObjC method id {} for method name {}", m.to(),
2144 method_decl->getQualifiedNameAsString());
2145
2146 diagram().add_active_participant(eid_t{method_decl->getID()});
2147
2148 return true;
2149}
2150
2152 model::message &m, const clang::CXXMemberCallExpr *method_call_expr)
2153{
2154 // Get callee declaration as methods parent
2155 const auto *method_decl = method_call_expr->getMethodDecl();
2156
2157 if (method_decl == nullptr)
2158 return false;
2159
2160 std::string method_name = method_decl->getQualifiedNameAsString();
2161
2162 const auto *callee_decl =
2163 method_decl != nullptr ? method_decl->getParent() : nullptr;
2164
2165 if (callee_decl == nullptr)
2166 return false;
2167
2168 if (!should_include(callee_decl) || !should_include(method_decl))
2169 return false;
2170
2171 m.set_to(eid_t{method_decl->getID()});
2172 m.set_message_name(method_decl->getNameAsString());
2174 method_call_expr->getCallReturnType(*context().get_ast_context())
2175 .getAsString());
2176
2177 LOG_TRACE("Set callee method id {} for method name {}", m.to(),
2178 method_decl->getQualifiedNameAsString());
2179
2180 diagram().add_active_participant(eid_t{method_decl->getID()});
2181
2182 return true;
2183}
2184
2186 model::message &m, const clang::CallExpr *expr)
2187{
2188 const auto *dependent_member_callee =
2189 clang::dyn_cast_or_null<clang::CXXDependentScopeMemberExpr>(
2190 expr->getCallee());
2191
2192 if (dependent_member_callee == nullptr)
2193 return false;
2194
2195 if (is_callee_valid_template_specialization(dependent_member_callee)) {
2196 if (const auto *tst = dependent_member_callee->getBaseType()
2197 ->getAs<clang::TemplateSpecializationType>();
2198 tst != nullptr) {
2199 const auto *template_declaration =
2200 tst->getTemplateName().getAsTemplateDecl();
2201
2202 std::string callee_method_full_name;
2203
2204 // First check if the primary template is already in the
2205 // participants map
2206 if (get_participant(template_declaration).has_value()) {
2207 callee_method_full_name = get_participant(template_declaration)
2208 .value()
2209 .full_name(false) +
2210 "::" + dependent_member_callee->getMember().getAsString();
2211
2212 for (const auto &[id, p] : diagram().participants()) {
2213 const auto p_full_name = p->full_name(false);
2214
2215 if (p_full_name.find(callee_method_full_name + "(") == 0) {
2216 // TODO: This selects the first matching template method
2217 // without considering arguments!!!
2218 m.set_to(id);
2219 break;
2220 }
2221 }
2222 }
2223 // Otherwise check if it is a smart pointer
2224 else if (is_smart_pointer(template_declaration)) {
2225 const auto *argument_template =
2226 template_declaration->getTemplateParameters()
2227 ->asArray()
2228 .front();
2229
2230 if (get_participant(argument_template).has_value()) {
2231 callee_method_full_name = get_participant(argument_template)
2232 .value()
2233 .full_name(false) +
2234 "::" +
2235 dependent_member_callee->getMember().getAsString();
2236
2237 for (const auto &[id, p] : diagram().participants()) {
2238 const auto p_full_name = p->full_name(false);
2239 if (p_full_name.find(callee_method_full_name + "(") ==
2240 0) {
2241 // TODO: This selects the first matching template
2242 // method without considering arguments!!!
2243 m.set_to(id);
2244 break;
2245 }
2246 }
2247 }
2248 else
2249 return false;
2250 }
2251
2253 dependent_member_callee->getMember().getAsString());
2254
2255 if (const auto maybe_id =
2256 get_unique_id(eid_t{template_declaration->getID()});
2257 maybe_id.has_value())
2258 diagram().add_active_participant(maybe_id.value());
2259 }
2260 }
2261 else {
2262 LOG_DBG("Skipping call due to unresolvable "
2263 "CXXDependentScopeMemberExpr at {}",
2264 expr->getBeginLoc().printToString(source_manager()));
2265 }
2266
2267 return true;
2268}
2269
2271 model::message &m, const clang::CallExpr *expr)
2272{
2273 const auto *callee_decl = expr->getCalleeDecl();
2274
2275 if (callee_decl == nullptr)
2276 return false;
2277
2278 const auto *callee_function = callee_decl->getAsFunction();
2279
2280 if (callee_function == nullptr)
2281 return false;
2282
2283 if (!should_include(callee_function))
2284 return false;
2285
2286 // Skip free functions declared in files outside of included paths
2287 if (config().combine_free_functions_into_file_participants() &&
2289 return false;
2290
2291 auto callee_name = callee_function->getQualifiedNameAsString() + "()";
2292
2293 m.set_to(id_mapper().resolve_or(eid_t{callee_function->getID()}));
2294 m.set_message_name(callee_name.substr(0, callee_name.size() - 2));
2295
2296 return true;
2297}
2298
2300 model::message &m, const clang::CallExpr *expr) const
2301{
2302 const auto *lambda_expr =
2303 clang::dyn_cast_or_null<clang::LambdaExpr>(expr->getCallee());
2304
2305 if (lambda_expr == nullptr)
2306 return true;
2307
2308 const auto lambda_class_id = eid_t{lambda_expr->getLambdaClass()->getID()};
2309 m.set_to(id_mapper().resolve_or(eid_t{lambda_class_id}));
2310
2311 return true;
2312}
2313
2315 model::message &m, const clang::CallExpr *expr) const
2316{
2317 // This is probably a template
2318 const auto *unresolved_expr =
2319 clang::dyn_cast_or_null<clang::UnresolvedLookupExpr>(expr->getCallee());
2320
2321 if (unresolved_expr != nullptr) {
2322 for (const auto *decl : unresolved_expr->decls()) {
2323 if (clang::dyn_cast_or_null<clang::FunctionTemplateDecl>(decl) !=
2324 nullptr) {
2325 const auto *ftd =
2326 clang::dyn_cast_or_null<clang::FunctionTemplateDecl>(decl);
2327 m.set_to(id_mapper().resolve_or(eid_t{ftd->getID()}));
2328 break;
2329 }
2330
2331 if (clang::dyn_cast_or_null<clang::FunctionDecl>(decl) != nullptr) {
2332 const auto *fd =
2333 clang::dyn_cast_or_null<clang::FunctionDecl>(decl);
2334 m.set_to(id_mapper().resolve_or(eid_t{fd->getID()}));
2335 break;
2336 }
2337
2338 LOG_DBG("Unknown unresolved lookup expression");
2339 }
2340 }
2341
2342 return true;
2343}
2344
2346 const clang::CXXDependentScopeMemberExpr *dependent_member_expr) const
2347{
2348 if (dependent_member_expr == nullptr)
2349 return false;
2350
2351 if (dependent_member_expr->getBaseType().isNull())
2352 return false;
2353
2354 const auto *tst = dependent_member_expr->getBaseType()
2355 ->getAs<clang::TemplateSpecializationType>();
2356
2357 if (tst == nullptr)
2358 return false;
2359
2360 return !(tst->isPointerType());
2361}
2362
2364 const clang::TemplateDecl *primary_template) const
2365{
2366 return primary_template->getQualifiedNameAsString().find(
2367 "std::unique_ptr") == 0 ||
2368 primary_template->getQualifiedNameAsString().find("std::shared_ptr") ==
2369 0 ||
2370 primary_template->getQualifiedNameAsString().find("std::weak_ptr") == 0;
2371}
2372
2373std::unique_ptr<clanguml::sequence_diagram::model::class_>
2375 clang::ObjCProtocolDecl *cls)
2376{
2377 assert(cls != nullptr);
2378
2379 auto c_ptr{std::make_unique<clanguml::sequence_diagram::model::class_>(
2380 config().using_namespace())};
2381 auto &c = *c_ptr;
2382
2383 auto qualified_name = cls->getQualifiedNameAsString();
2384
2385 c.is_objc_interface();
2386
2387 c.set_name(cls->getQualifiedNameAsString());
2388 c.set_id(common::to_id(*cls));
2389
2390 process_comment(*cls, c);
2391 set_source_location(*cls, c);
2392
2393 if (c.skip())
2394 return {};
2395
2396 c.set_style(c.style_spec());
2397
2398 return c_ptr;
2399}
2400
2401std::unique_ptr<clanguml::sequence_diagram::model::class_>
2403 clang::ObjCInterfaceDecl *cls)
2404{
2405 assert(cls != nullptr);
2406
2407 auto c_ptr{std::make_unique<clanguml::sequence_diagram::model::class_>(
2408 config().using_namespace())};
2409 auto &c = *c_ptr;
2410
2411 auto qualified_name = cls->getQualifiedNameAsString();
2412
2413 c.is_objc_interface(true);
2414
2415 c.set_name(cls->getQualifiedNameAsString());
2416 c.set_id(common::to_id(*cls));
2417
2418 process_comment(*cls, c);
2419 set_source_location(*cls, c);
2420
2421 if (c.skip())
2422 return {};
2423
2424 c.set_style(c.style_spec());
2425
2426 return c_ptr;
2427}
2428
2429std::unique_ptr<clanguml::sequence_diagram::model::class_>
2431{
2432 assert(cls != nullptr);
2433
2434 auto c_ptr{std::make_unique<clanguml::sequence_diagram::model::class_>(
2435 config().using_namespace())};
2436 auto &c = *c_ptr;
2437
2438 auto qualified_name = cls->getQualifiedNameAsString();
2439
2440 if (!cls->isLambda())
2441 if (!should_include(cls))
2442 return {};
2443
2444 auto ns = common::get_tag_namespace(*cls);
2445
2446 if (cls->isLambda() && !diagram().should_include(ns | "lambda"))
2447 return {};
2448
2449 const auto *parent = cls->getParent();
2450
2451 if ((parent != nullptr) && parent->isRecord()) {
2452 // Here we have 3 options, either:
2453 // - the parent is a regular C++ class/struct
2454 // - the parent is a class template declaration/specialization
2455 // - the parent is a lambda (i.e. this is a nested lambda expression)
2456 std::optional<eid_t> id_opt;
2457 const auto *parent_record_decl =
2458 clang::dyn_cast<clang::RecordDecl>(parent);
2459
2460 assert(parent_record_decl != nullptr);
2461
2462 const eid_t ast_id{parent_record_decl->getID()};
2463
2464 // First check if the parent has been added to the diagram as
2465 // regular class
2466 id_opt = get_unique_id(ast_id);
2467
2468 // If not, check if the parent template declaration is in the model
2469 if (!id_opt &&
2470 (parent_record_decl->getDescribedTemplate() != nullptr)) {
2471 parent_record_decl->getDescribedTemplate()->getID();
2472 if (parent_record_decl->getDescribedTemplate() != nullptr)
2473 id_opt = get_unique_id(ast_id);
2474 }
2475
2476 if (!id_opt)
2477 return {};
2478
2479 auto parent_class =
2480 diagram()
2482 *id_opt);
2483
2484 if (!parent_class) {
2485 return {};
2486 }
2487
2488 c.set_namespace(ns);
2489 if (cls->getNameAsString().empty()) {
2490 // Nested structs can be anonymous
2491 if (anonymous_struct_relationships_.count(cls->getID()) > 0) {
2492 const auto &[label, hint, access] =
2493 anonymous_struct_relationships_[cls->getID()];
2494
2495 c.set_name(parent_class.value().name() +
2496 "::" + fmt::format("({})", label));
2497
2498 parent_class.value().add_relationship(
2499 {hint, common::to_id(c.full_name(false)), access, label});
2500 }
2501 else
2502 c.set_name(parent_class.value().name() + "::" +
2503 fmt::format(
2504 "(anonymous_{})", std::to_string(cls->getID())));
2505 }
2506 else {
2507 c.set_name(
2508 parent_class.value().name() + "::" + cls->getNameAsString());
2509 }
2510
2511 c.set_id(common::to_id(c.full_name(false)));
2512
2513 c.nested(true);
2514 }
2515 else if (cls->isLambda()) {
2516 c.is_lambda(true);
2517 if (cls->getParent() != nullptr) {
2518 const auto type_name = make_lambda_name(cls);
2519
2520 c.set_name(type_name);
2521 c.set_namespace(ns);
2522 c.set_id(common::to_id(c.full_name(false)));
2523 }
2524 else {
2525 LOG_WARN("Cannot find parent declaration for lambda {}",
2526 cls->getQualifiedNameAsString());
2527 return {};
2528 }
2529 }
2530 // This is equivalent to parent != nullptr and parent->isFunctionDecl()
2531 else if (cls->isLocalClass() != nullptr) {
2532 const auto *func_declaration = cls->isLocalClass();
2533
2534 eid_t local_parent_id{int64_t{}};
2535
2536 if (common::is_lambda_method(func_declaration)) {
2537 LOG_DBG("The local class is defined in a lambda operator()");
2538 const auto *method_declaration =
2539 clang::dyn_cast<clang::CXXMethodDecl>(func_declaration);
2540
2541 if (method_declaration != nullptr &&
2542 method_declaration->getParent() != nullptr) {
2543 local_parent_id = method_declaration->getParent()->getID();
2544 }
2545 }
2546 else {
2547 local_parent_id = func_declaration->getID();
2548 }
2549
2550 eid_t parent_id = get_unique_id(local_parent_id).has_value()
2551 ? *get_unique_id(local_parent_id) // NOLINT
2552 : local_parent_id;
2553
2554 const auto &func_model =
2555 diagram().get_participant<model::participant>(parent_id);
2556
2557 if (!func_model.has_value())
2558 return {};
2559
2560 LOG_DBG("Visiting local class declaration: {} in {}",
2561 cls->getQualifiedNameAsString(),
2562 func_model.value().full_name(false));
2563
2564 auto local_cls_ns = func_model.value().get_namespace();
2565
2566 c.set_name(
2567 func_model.value().full_name_no_ns(), common::get_tag_name(*cls));
2568 c.set_namespace(local_cls_ns);
2569 c.set_id(common::to_id(c.full_name(false)));
2570 c.nested(true);
2571 }
2572 else {
2573 c.set_name(common::get_tag_name(*cls));
2574 c.set_namespace(ns);
2575 c.set_id(common::to_id(c.full_name(false)));
2576 }
2577
2578 c.is_struct(cls->isStruct());
2579
2580 process_comment(*cls, c);
2581 set_source_location(*cls, c);
2582
2583 if (c.skip())
2584 return {};
2585
2586 c.set_style(c.style_spec());
2587
2588 return c_ptr;
2589}
2590
2591void translation_unit_visitor::set_unique_id(int64_t local_id, eid_t global_id)
2592{
2593 LOG_TRACE("Setting local element mapping {} --> {}", local_id, global_id);
2594
2595 assert(global_id.is_global());
2596
2597 id_mapper().add(local_id, global_id);
2598}
2599
2601 eid_t local_id) const
2602{
2603 if (local_id.is_global())
2604 return local_id;
2605
2606 return id_mapper().get_global_id(local_id);
2607}
2608
2609std::unique_ptr<model::function_template>
2611 const clang::FunctionTemplateDecl &declaration)
2612{
2613 auto function_template_model_ptr =
2614 std::make_unique<sequence_diagram::model::function_template>(
2615 config().using_namespace());
2616
2617 set_qualified_name(declaration, *function_template_model_ptr);
2618
2620 *function_template_model_ptr, declaration);
2621
2622 function_template_model_ptr->return_type(
2623 common::to_string(declaration.getAsFunction()->getReturnType(),
2624 declaration.getASTContext()));
2625
2626 if (declaration.getAsFunction() != nullptr) {
2628 *declaration.getAsFunction(), *function_template_model_ptr);
2629 }
2630
2631 return function_template_model_ptr;
2632}
2633
2634std::unique_ptr<model::function_template>
2636 const clang::FunctionDecl &declaration)
2637{
2638 auto template_instantiation_ptr =
2639 std::make_unique<model::function_template>(config().using_namespace());
2640 auto &template_instantiation = *template_instantiation_ptr;
2641
2642 set_qualified_name(declaration, template_instantiation);
2643
2644 tbuilder().build(declaration, template_instantiation, &declaration,
2645 declaration.getPrimaryTemplate(),
2646 declaration.getTemplateSpecializationArgs()->asArray(),
2647 common::to_string(&declaration));
2648
2649 process_function_parameters(declaration, *template_instantiation_ptr);
2650
2651 return template_instantiation_ptr;
2652}
2653
2654std::unique_ptr<model::function> translation_unit_visitor::build_function_model(
2655 const clang::FunctionDecl &declaration)
2656{
2657 auto function_model_ptr =
2658 std::make_unique<sequence_diagram::model::function>(
2659 config().using_namespace());
2660
2661 common::model::namespace_ ns{declaration.getQualifiedNameAsString()};
2662 function_model_ptr->set_name(ns.name());
2663 ns.pop_back();
2664 function_model_ptr->set_namespace(ns);
2665
2666 function_model_ptr->return_type(common::to_string(
2667 declaration.getReturnType(), declaration.getASTContext()));
2668
2669 process_function_parameters(declaration, *function_model_ptr);
2670
2671 if (declaration.isVariadic()) {
2672 function_model_ptr->add_parameter("...");
2673 }
2674
2675 return function_model_ptr;
2676}
2677
2678std::unique_ptr<model::class_>
2680 clang::ClassTemplateSpecializationDecl *cls)
2681{
2682 auto c_ptr{std::make_unique<model::class_>(config().using_namespace())};
2683
2685
2686 auto &template_instantiation = *c_ptr;
2687
2688 // TODO: refactor to method get_qualified_name()
2689 auto qualified_name = cls->getQualifiedNameAsString();
2690 util::replace_all(qualified_name, "(anonymous namespace)", "");
2691 util::replace_all(qualified_name, "::::", "::");
2692
2693 common::model::namespace_ ns{qualified_name};
2694 ns.pop_back();
2695 template_instantiation.set_name(cls->getNameAsString());
2696 template_instantiation.set_namespace(ns);
2697
2698 template_instantiation.is_struct(cls->isStruct());
2699
2700 process_comment(*cls, template_instantiation);
2701 set_source_location(*cls, template_instantiation);
2702 set_owning_module(*cls, template_instantiation);
2703
2704 if (template_instantiation.skip())
2705 return {};
2706
2707 template_instantiation.set_id(
2708 common::to_id(template_instantiation.full_name(false)));
2709
2710 set_unique_id(cls->getID(), template_instantiation.id());
2711
2712 return c_ptr;
2713}
2714
2716 const std::string &full_name) const
2717{
2718 return config().simplify_template_type(full_name);
2719}
2720
2722 const clang::SourceLocation &source_location) const
2723{
2724 const auto file_line =
2725 source_manager().getSpellingLineNumber(source_location);
2726 const auto file_column =
2727 source_manager().getSpellingColumnNumber(source_location);
2728 const std::string file_name =
2729 config()
2730 .make_path_relative(
2731 source_manager().getFilename(source_location).str())
2732 .string();
2733 return fmt::format("{}:{}:{}", file_name, file_line, file_column);
2734}
2735
2737 const clang::CXXRecordDecl *cls) const
2738{
2739 std::string result;
2740 const auto location = cls->getLocation();
2741 const std::string source_location{lambda_source_location(location)};
2742
2743 const auto maybe_lambda_caller_id = context().lambda_caller_id();
2744 if (maybe_lambda_caller_id.has_value()) {
2745 // Parent is also a lambda (this id points to a lambda operator())
2746 std::string parent_lambda_class_name{"()"};
2747 if (diagram().get_participant<model::method>(
2748 maybe_lambda_caller_id.value())) {
2749 auto parent_lambda_class_id =
2750 diagram()
2751 .get_participant<model::method>(
2752 maybe_lambda_caller_id.value())
2753 .value()
2754 .class_id();
2755
2756 if (diagram().get_participant<model::class_>(
2757 parent_lambda_class_id)) {
2758 parent_lambda_class_name =
2759 diagram()
2760 .get_participant<model::class_>(parent_lambda_class_id)
2761 .value()
2762 .full_name(false);
2763 }
2764 }
2765
2766 result = fmt::format(
2767 "{}##(lambda {})", parent_lambda_class_name, source_location);
2768 }
2769 else if (context().caller_id().value() != 0 &&
2770 get_participant(context().caller_id()).has_value()) {
2771 auto parent_full_name =
2772 get_participant(context().caller_id()).value().full_name_no_ns();
2773
2774 result =
2775 fmt::format("{}##(lambda {})", parent_full_name, source_location);
2776 }
2777 else {
2778 result = fmt::format("(lambda {})", source_location);
2779 }
2780
2781 return result;
2782}
2783
2785 clang::CallExpr *expr, model::message &&m)
2786{
2787 call_expr_message_map_[expr].push_back(std::move(m));
2788}
2789
2791 clang::CXXConstructExpr *expr, model::message &&m)
2792{
2793 construct_expr_message_map_.emplace(expr, std::move(m));
2794}
2795
2797 clang::ObjCMessageExpr *expr, model::message &&m)
2798{
2799 objc_message_map_.emplace(expr, std::move(m));
2800}
2801
2803 clang::ReturnStmt *stmt, model::message &&m)
2804{
2805 return_stmt_message_map_.emplace(stmt, std::move(m));
2806}
2807
2809 clang::CoreturnStmt *stmt, model::message &&m)
2810{
2811 co_return_stmt_message_map_.emplace(stmt, std::move(m));
2812}
2813
2815 clang::CoyieldExpr *expr, model::message &&m)
2816{
2817 co_yield_stmt_message_map_.emplace(expr, std::move(m));
2818}
2819
2821 clang::CoawaitExpr *expr, model::message &&m)
2822{
2823 co_await_stmt_message_map_.emplace(expr, std::move(m));
2824}
2825
2827{
2828 assert(expr != nullptr);
2829
2830 // Skip if no message was generated from this expr
2831 if (call_expr_message_map_.find(expr) == call_expr_message_map_.end()) {
2832 return;
2833 }
2834
2835 if (call_expr_message_map_.at(expr).empty())
2836 return;
2837
2838 while (!call_expr_message_map_.at(expr).empty()) {
2839 auto msg = call_expr_message_map_.at(expr).front();
2840
2841 auto caller_id = msg.from();
2842
2843 if (caller_id == 0)
2844 return;
2845
2846 if (diagram().has_activity(caller_id))
2847 diagram().get_activity(caller_id).add_message(std::move(msg));
2848 else
2849 LOG_DBG("Skipping message due to missing activity: {}", caller_id);
2850
2851 call_expr_message_map_.at(expr).pop_front();
2852 }
2853
2854 call_expr_message_map_.erase(expr);
2855}
2856
2858 clang::CXXConstructExpr *expr)
2859{
2860 assert(expr != nullptr);
2861
2862 // Skip if no message was generated from this expr
2863 if (construct_expr_message_map_.find(expr) ==
2865 return;
2866 }
2867
2868 auto msg = std::move(construct_expr_message_map_.at(expr));
2869
2870 auto caller_id = msg.from();
2871 diagram().get_activity(caller_id).add_message(std::move(msg));
2872
2873 construct_expr_message_map_.erase(expr);
2874}
2875
2877{
2878 assert(stmt != nullptr);
2879
2880 // Skip if no message was generated from this expr
2881 if (return_stmt_message_map_.find(stmt) == return_stmt_message_map_.end()) {
2882 return;
2883 }
2884
2885 auto msg = std::move(return_stmt_message_map_.at(stmt));
2886
2887 auto caller_id = msg.from();
2888 diagram().get_activity(caller_id).add_message(std::move(msg));
2889
2890 return_stmt_message_map_.erase(stmt);
2891}
2892
2894{
2895 assert(stmt != nullptr);
2896
2897 // Skip if no message was generated from this expr
2898 if (co_return_stmt_message_map_.find(stmt) ==
2900 return;
2901 }
2902
2903 auto msg = std::move(co_return_stmt_message_map_.at(stmt));
2904
2905 auto caller_id = msg.from();
2906 diagram().get_activity(caller_id).add_message(std::move(msg));
2907
2908 co_return_stmt_message_map_.erase(stmt);
2909}
2910
2912{
2913 assert(expr != nullptr);
2914
2915 // Skip if no message was generated from this expr
2916 if (co_yield_stmt_message_map_.find(expr) ==
2918 return;
2919 }
2920
2921 auto msg = std::move(co_yield_stmt_message_map_.at(expr));
2922
2923 auto caller_id = msg.from();
2924 diagram().get_activity(caller_id).add_message(std::move(msg));
2925
2926 co_yield_stmt_message_map_.erase(expr);
2927}
2928
2930{
2931 assert(expr != nullptr);
2932
2933 // Skip if no message was generated from this expr
2934 if (co_await_stmt_message_map_.find(expr) ==
2936 return;
2937 }
2938
2939 auto msg = std::move(co_await_stmt_message_map_.at(expr));
2940
2941 auto caller_id = msg.from();
2942 diagram().get_activity(caller_id).add_message(std::move(msg));
2943
2944 co_await_stmt_message_map_.erase(expr);
2945}
2946
2948 clang::ObjCMessageExpr *expr)
2949{
2950 assert(expr != nullptr);
2951
2952 // Skip if no message was generated from this expr
2953 if (objc_message_map_.find(expr) == objc_message_map_.end()) {
2954 return;
2955 }
2956
2957 auto msg = std::move(objc_message_map_.at(expr));
2958
2959 auto caller_id = msg.from();
2960 diagram().get_activity(caller_id).add_message(std::move(msg));
2961
2962 objc_message_map_.erase(expr);
2963}
2964
2966{
2968
2969 // Change all messages with target set to an id of a lambda expression to
2970 // to the ID of their operator() - this is necessary, as some calls to
2971 // lambda expressions are visited before the actual lambda expressions
2972 // are visited...
2974
2976
2977 if (config().inline_lambda_messages())
2978 diagram().inline_lambda_operator_calls();
2979}
2980
2982{
2983 for (auto &[id, activity] : diagram().sequences()) {
2984 for (auto &m : activity.messages()) {
2985 auto participant = diagram().get_participant<model::class_>(m.to());
2986
2987 if (participant && participant.value().is_lambda() &&
2988 participant.value().lambda_operator_id().value() != 0) {
2989 LOG_DBG("Changing lambda expression target id from {} to {}",
2990 m.to(), participant.value().lambda_operator_id());
2991
2992 m.set_to(participant.value().lambda_operator_id());
2993 m.set_message_name("operator()");
2994
2996 }
2997 }
2998 }
2999}
3000
3002{
3003 std::set<eid_t> active_participants_unique;
3004
3005 // Change all active participants AST local ids to diagram global ids
3006 for (auto id : diagram().active_participants()) {
3007 if (const auto unique_id = get_unique_id(id);
3008 !id.is_global() && unique_id.has_value()) {
3009 active_participants_unique.emplace(unique_id.value());
3010 }
3011 else if (id.is_global()) {
3012 active_participants_unique.emplace(id);
3013 }
3014 }
3015
3016 diagram().active_participants() = std::move(active_participants_unique);
3017
3018 // Change all message callees AST local ids to diagram global ids
3019 for (auto &[id, activity] : diagram().sequences()) {
3020 for (auto &m : activity.messages()) {
3021 if (const auto unique_id = get_unique_id(m.to());
3022 !m.to().is_global() && unique_id.has_value()) {
3023 m.set_to(unique_id.value());
3024 assert(m.to().is_global());
3025 }
3026 }
3027 }
3028}
3029
3031{
3032 // Translate reverse activity call graph local ids to global ids
3033 std::map<eid_t, std::set<eid_t>> acs;
3034 for (const auto &[id, caller_ids] : activity_callers_) {
3035 auto unique_id = get_unique_id(id);
3036 if (!unique_id)
3037 continue;
3038 std::set<eid_t> unique_caller_ids;
3039 for (const auto &caller_id : caller_ids) {
3040 auto unique_caller_id = get_unique_id(caller_id);
3041 if (unique_caller_id)
3042 unique_caller_ids.emplace(*unique_caller_id);
3043 }
3044 acs.emplace(*unique_id, std::move(unique_caller_ids));
3045 }
3046
3047 // Change all message callees AST local ids to diagram global ids
3048 for (auto &[id, activity] : diagram().sequences()) {
3049 assert(id.is_global());
3050
3051 if (acs.count(id) > 0) {
3052 activity.set_callers(acs.at(id));
3053 }
3054 }
3055}
3056
3057std::unique_ptr<clanguml::sequence_diagram::model::method>
3059 clang::CXXMethodDecl *declaration)
3060{
3061 auto method_model_ptr = std::make_unique<sequence_diagram::model::method>(
3062 config().using_namespace());
3063
3064 common::model::namespace_ ns{declaration->getQualifiedNameAsString()};
3065 auto method_name = ns.name();
3066 method_model_ptr->set_method_name(method_name);
3067 ns.pop_back();
3068 method_model_ptr->set_name(ns.name());
3069 ns.pop_back();
3070 method_model_ptr->set_namespace(ns);
3071
3072 method_model_ptr->is_defaulted(declaration->isDefaulted());
3073 method_model_ptr->is_assignment(declaration->isCopyAssignmentOperator() ||
3074 declaration->isMoveAssignmentOperator());
3075 method_model_ptr->is_const(declaration->isConst());
3076 method_model_ptr->is_static(declaration->isStatic());
3077 method_model_ptr->is_operator(declaration->isOverloadedOperator());
3078 method_model_ptr->is_constructor(
3079 clang::dyn_cast<clang::CXXConstructorDecl>(declaration) != nullptr);
3080
3081 method_model_ptr->is_void(declaration->getReturnType()->isVoidType());
3082
3083 method_model_ptr->return_type(common::to_string(
3084 declaration->getReturnType(), declaration->getASTContext()));
3085
3086 process_function_parameters(*declaration, *method_model_ptr);
3087
3088 return method_model_ptr;
3089}
3090
3091std::unique_ptr<clanguml::sequence_diagram::model::objc_method>
3093 clang::ObjCMethodDecl *declaration)
3094{
3095 auto method_model_ptr =
3096 std::make_unique<sequence_diagram::model::objc_method>(
3097 config().using_namespace());
3098
3099 common::model::namespace_ ns{declaration->getQualifiedNameAsString()};
3100 auto method_name = ns.name();
3101 method_model_ptr->set_method_name(method_name);
3102 ns.pop_back();
3103 method_model_ptr->set_name(ns.name());
3104 method_model_ptr->set_namespace({});
3105
3106 clang::Decl *parent_decl = declaration->getClassInterface();
3107
3108 if (parent_decl == nullptr) {
3109 LOG_DBG("Cannot find ObjC interface for method, probably it is a "
3110 "protocol {} [{}]",
3111 method_name,
3112 declaration->getLocation().printToString(source_manager()));
3113 return {};
3114 }
3115
3116 LOG_DBG("Getting ObjC method's interface with local id {}",
3117 parent_decl->getID());
3118
3119 const auto maybe_method_class = get_participant<model::class_>(parent_decl);
3120
3121 if (!maybe_method_class) {
3122 LOG_DBG("Cannot find parent class_ for method {} in class {}",
3123 declaration->getQualifiedNameAsString(),
3124 declaration->getClassInterface()->getQualifiedNameAsString());
3125 return {};
3126 }
3127
3128 const auto &method_class = maybe_method_class.value();
3129
3130 method_model_ptr->is_void(declaration->getReturnType()->isVoidType());
3131
3132 method_model_ptr->set_class_id(method_class.id());
3133 method_model_ptr->set_class_full_name(method_class.full_name(false));
3134 method_model_ptr->set_name(get_participant(method_model_ptr->class_id())
3135 .value()
3136 .full_name_no_ns() +
3137 "::" + declaration->getNameAsString());
3138
3139 method_model_ptr->return_type(common::to_string(
3140 declaration->getReturnType(), declaration->getASTContext()));
3141
3142 for (const auto *param : declaration->parameters()) {
3143 auto parameter_type =
3144 common::to_string(param->getType(), param->getASTContext());
3146 parameter_type = simplify_system_template(parameter_type);
3147
3148 std::string parameter_str = parameter_type;
3149 if (config().generate_method_argument_names()) {
3150 parameter_str += " " + param->getNameAsString();
3151 }
3152
3153 method_model_ptr->add_parameter(parameter_type);
3154 }
3155
3156 return method_model_ptr;
3157}
3158
3159std::unique_ptr<clanguml::sequence_diagram::model::method>
3160translation_unit_visitor::create_method_model(clang::CXXMethodDecl *declaration)
3161{
3162 auto method_model_ptr = std::make_unique<sequence_diagram::model::method>(
3163 config().using_namespace());
3164
3165 common::model::namespace_ ns{declaration->getQualifiedNameAsString()};
3166 auto method_name = ns.name();
3167 method_model_ptr->set_method_name(method_name);
3168 ns.pop_back();
3169 method_model_ptr->set_name(ns.name());
3170 ns.pop_back();
3171 method_model_ptr->set_namespace(ns);
3172
3173 method_model_ptr->is_defaulted(declaration->isDefaulted());
3174 method_model_ptr->is_assignment(declaration->isCopyAssignmentOperator() ||
3175 declaration->isMoveAssignmentOperator());
3176 method_model_ptr->is_const(declaration->isConst());
3177 method_model_ptr->is_static(declaration->isStatic());
3178 method_model_ptr->is_operator(declaration->isOverloadedOperator());
3179 method_model_ptr->is_constructor(
3180 clang::dyn_cast<clang::CXXConstructorDecl>(declaration) != nullptr);
3181 method_model_ptr->is_coroutine(common::is_coroutine(*declaration));
3182
3183 clang::Decl *parent_decl = declaration->getParent();
3184
3185 if (context().current_class_template_decl_ != nullptr)
3186 parent_decl = context().current_class_template_decl_;
3187
3188 LOG_DBG("Getting method's class with local id {}", parent_decl->getID());
3189
3190 const auto maybe_method_class = get_participant<model::class_>(parent_decl);
3191
3192 if (!maybe_method_class) {
3193 LOG_DBG("Cannot find parent class_ for method {} in class {}",
3194 declaration->getQualifiedNameAsString(),
3195 declaration->getParent()->getQualifiedNameAsString());
3196 return {};
3197 }
3198
3199 LOG_DBG(
3200 "Found method class: {}", maybe_method_class.value().full_name(false));
3201
3202 const auto &method_class = maybe_method_class.value();
3203
3204 method_model_ptr->is_void(declaration->getReturnType()->isVoidType());
3205
3206 method_model_ptr->set_class_id(method_class.id());
3207 method_model_ptr->set_class_full_name(method_class.full_name(false));
3208 method_model_ptr->set_name(get_participant(method_model_ptr->class_id())
3209 .value()
3210 .full_name_no_ns() +
3211 "::" + declaration->getNameAsString());
3212
3213 method_model_ptr->return_type(common::to_string(
3214 declaration->getReturnType(), declaration->getASTContext()));
3215
3216 process_function_parameters(*declaration, *method_model_ptr);
3217
3218 return method_model_ptr;
3219}
3220
3222 const clang::FunctionDecl &declaration, model::function &method_model) const
3223{
3224 for (const auto *param : declaration.parameters()) {
3225 auto parameter_type =
3226 common::to_string(param->getType(), param->getASTContext());
3228 parameter_type = simplify_system_template(parameter_type);
3229
3230 std::string parameter_str = config().using_namespace().relative(
3231 simplify_system_template(parameter_type));
3232 if (config().generate_method_argument_names()) {
3233 parameter_str += " " + param->getNameAsString();
3234 }
3235
3236 method_model.add_parameter(parameter_str);
3237 }
3238}
3239
3240bool translation_unit_visitor::should_include(const clang::TagDecl *decl) const
3241{
3243 dynamic_cast<const clang::NamedDecl *>(decl));
3244}
3245
3247 const clang::ObjCContainerDecl *decl) const
3248{
3250 dynamic_cast<const clang::NamedDecl *>(decl));
3251}
3252
3254 const clang::LambdaExpr *expr) const
3255{
3256 if (context().caller_id() == 0)
3257 return false;
3258
3259 if (!context().valid())
3260 return false;
3261
3262 const auto expr_file = expr->getBeginLoc().printToString(source_manager());
3263
3264 if (!diagram().should_include(
3266 return false;
3267
3268 return true;
3269}
3270
3272 const clang::ObjCMessageExpr *expr) const
3273{
3274 if (context().caller_id() == 0)
3275 return false;
3276
3277 if (!context().valid())
3278 return false;
3279
3280 const auto expr_file = expr->getBeginLoc().printToString(source_manager());
3281
3282 if (!diagram().should_include(
3284 return false;
3285
3286 return true;
3287}
3288
3289bool translation_unit_visitor::should_include(const clang::CallExpr *expr) const
3290{
3291 if (context().caller_id() == 0)
3292 return false;
3293
3294 // Skip casts, moves and such
3295 if (expr->isCallToStdMove())
3296 return false;
3297
3298 if (expr->isImplicitCXXThis())
3299 return false;
3300
3301 if (clang::dyn_cast_or_null<clang::ImplicitCastExpr>(expr) != nullptr)
3302 return false;
3303
3304 if (!context().valid())
3305 return false;
3306
3307 const auto expr_file = expr->getBeginLoc().printToString(source_manager());
3308
3309 if (!diagram().should_include(
3311 return false;
3312
3313 const auto *callee_decl = expr->getCalleeDecl();
3314
3315 if (callee_decl != nullptr) {
3316 const auto *callee_function = callee_decl->getAsFunction();
3317
3318 if ((callee_function == nullptr) || !should_include(callee_function)) {
3319 LOG_DBG("Skipping call expression at {}",
3320 expr->getBeginLoc().printToString(source_manager()));
3321 return false;
3322 }
3323
3324 return should_include(callee_function);
3325 }
3326
3327 return true;
3328}
3329
3331 const clang::CXXMethodDecl *decl) const
3332{
3333 if (!should_include(decl->getParent()))
3334 return false;
3335
3336 if (!diagram().should_include(
3337 common::access_specifier_to_access_t(decl->getAccess())))
3338 return false;
3339
3340 LOG_DBG("Including method {}", decl->getQualifiedNameAsString());
3341
3342 return true;
3343}
3344
3346 const clang::ObjCMethodDecl *decl) const
3347{
3348 if (!diagram().should_include(
3349 common::access_specifier_to_access_t(decl->getAccess())))
3350 return false;
3351
3352 LOG_DBG("Including ObjC method {}", decl->getQualifiedNameAsString());
3353
3354 return true;
3355}
3356
3358 const clang::FunctionDecl *decl) const
3359{
3361}
3362
3364 const clang::FunctionTemplateDecl *decl) const
3365{
3366 return visitor_specialization_t::should_include(decl->getAsFunction());
3367}
3368
3370 const clang::ClassTemplateDecl *decl) const
3371{
3373}
3374
3375std::optional<std::pair<unsigned int, std::string>>
3377 const clang::ASTContext &context, const eid_t caller_id,
3378 const clang::Stmt *stmt)
3379{
3380 const auto *raw_comment =
3382
3383 if (raw_comment == nullptr)
3384 return {};
3385
3386 if (!caller_id.is_global() &&
3388 .emplace(caller_id.ast_local_value(), raw_comment)
3389 .second) {
3390 return {};
3391 }
3392
3393 const auto &[decorators, stripped_comment] = decorators::parse(
3394 raw_comment->getFormattedText(sm, sm.getDiagnostics()));
3395
3396 if (stripped_comment.empty())
3397 return {};
3398
3399 return {{raw_comment->getBeginLoc().getHashValue(), stripped_comment}};
3400}
3401} // namespace clanguml::sequence_diagram::visitor