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