31 , template_builder_{diagram, config, *this}
35std::unique_ptr<sequence_diagram::model::class_>
37 const clang::NamedDecl * )
const
39 return std::make_unique<sequence_diagram::model::class_>(
40 config().using_namespace());
59 clang::ObjCProtocolDecl *declaration)
64 LOG_TRACE(
"Visiting ObjC interface declaration at {}",
69 if (!objc_protocol_model_ptr)
74 const auto class_id = objc_protocol_model_ptr->id();
85 : *objc_protocol_model_ptr;
88 LOG_DBG(
"Adding ObjC protocol participant {} with id {}",
89 class_model.full_name(
false), class_model.id());
91 assert(class_model.id() == class_id);
96 diagram().add_participant(std::move(objc_protocol_model_ptr));
99 LOG_DBG(
"Skipping ObjC protocol {} with id {}",
100 class_model.full_name(
true), class_id);
107 clang::ObjCInterfaceDecl *declaration)
112 LOG_TRACE(
"Visiting ObjC interface declaration at {}",
119 if (!objc_interface_model_ptr)
124 const auto class_id = objc_interface_model_ptr->id();
135 : *objc_interface_model_ptr;
138 LOG_DBG(
"Adding ObjC interface participant {} with id {}",
139 class_model.full_name(
false), class_model.id());
141 assert(class_model.id() == class_id);
146 diagram().add_participant(std::move(objc_interface_model_ptr));
149 LOG_DBG(
"Skipping ObjC interface {} with id {}",
150 class_model.full_name(
true), class_id);
157 clang::CXXRecordDecl *declaration)
163 if (declaration->isTemplated() &&
164 declaration->getDescribedTemplate() !=
nullptr) {
170 if (declaration->isLocalClass() !=
nullptr)
173 LOG_TRACE(
"Visiting class declaration at {}",
180 if (!class_model_ptr)
185 const auto class_id = class_model_ptr->id();
198 if (!declaration->isCompleteDefinition()) {
206 LOG_DBG(
"Adding class participant {} with id {}",
207 class_model.full_name(
false), class_model.id());
209 assert(class_model.id() == class_id);
214 diagram().add_participant(std::move(class_model_ptr));
217 LOG_DBG(
"Skipping class {} with id {}", class_model.full_name(
true),
225 clang::ClassTemplateDecl *declaration)
230 LOG_TRACE(
"Visiting class template declaration {} at {} [{}]",
231 declaration->getQualifiedNameAsString(),
233 (
void *)declaration);
237 if (!class_model_ptr)
242 const auto class_full_name = class_model_ptr->full_name(
false);
247 class_model_ptr->set_id(
id);
251 if (!declaration->getTemplatedDecl()->isCompleteDefinition()) {
258 LOG_DBG(
"Adding class template participant {} with id {}",
259 class_full_name,
id);
264 diagram().add_participant(std::move(class_model_ptr));
271 clang::ClassTemplateSpecializationDecl *declaration)
276 LOG_TRACE(
"Visiting template specialization declaration {} at {}",
277 declaration->getQualifiedNameAsString(),
281 if (declaration->isLocalClass() !=
nullptr)
284 auto template_specialization_ptr =
287 if (!template_specialization_ptr)
290 const auto class_full_name = template_specialization_ptr->full_name(
false);
293 template_specialization_ptr->set_id(
id);
297 if (!declaration->isCompleteDefinition()) {
299 id, std::move(template_specialization_ptr));
306 "Adding class template specialization participant {} with id {}",
307 class_full_name,
id);
312 diagram().add_participant(std::move(template_specialization_ptr));
319 clang::ObjCMethodDecl *declaration)
324 auto context_backup =
context();
326 RecursiveASTVisitor<translation_unit_visitor>::TraverseObjCMethodDecl(
335 clang::ObjCMethodDecl *declaration)
340 LOG_TRACE(
"Visiting ObjC method {} in class",
341 declaration->getQualifiedNameAsString());
347 if (!method_model_ptr)
354 const auto method_full_name = method_model_ptr->full_name(
false);
360 LOG_TRACE(
"Set id {} --> {} for method name {} [{}]", declaration->getID(),
361 method_model_ptr->id(), method_full_name,
362 declaration->isThisDeclarationADefinition());
368 diagram().add_participant(std::move(method_model_ptr));
374 clang::CXXMethodDecl *declaration)
379 auto context_backup =
context();
381 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXMethodDecl(
390 clang::CXXMethodDecl *declaration)
395 if (!declaration->isThisDeclarationADefinition()) {
396 if (
auto *declaration_definition = declaration->getDefinition();
397 declaration_definition !=
nullptr) {
398 if (
auto *method_definition = clang::dyn_cast<clang::CXXMethodDecl>(
399 declaration_definition);
400 method_definition !=
nullptr) {
401 LOG_DBG(
"Calling VisitCXXMethodDecl recursively for forward "
409 LOG_TRACE(
"Visiting method {} in class {} [{}]",
410 declaration->getQualifiedNameAsString(),
411 declaration->getParent()->getQualifiedNameAsString(),
412 (
void *)declaration->getParent());
418 if (!method_model_ptr)
425 const auto method_full_name = method_model_ptr->full_name(
false);
431 if (declaration->isThisDeclarationADefinition()) {
433 declaration->getFirstDecl()->getID(), method_model_ptr->id());
438 LOG_TRACE(
"Set id {} --> {} for method name {} [{}]", declaration->getID(),
439 method_model_ptr->id(), method_full_name,
440 declaration->isThisDeclarationADefinition());
446 diagram().add_participant(std::move(method_model_ptr));
452 clang::FunctionDecl *declaration)
457 auto context_backup =
context();
459 RecursiveASTVisitor<translation_unit_visitor>::TraverseFunctionDecl(
468 clang::FunctionDecl *declaration)
470 if (declaration->isCXXClassMember())
476 if (!declaration->isThisDeclarationADefinition()) {
477 if (
auto *declaration_definition = declaration->getDefinition();
478 declaration_definition !=
nullptr)
480 static_cast<clang::FunctionDecl *
>(declaration_definition));
483 LOG_TRACE(
"Visiting function declaration {} at {}",
484 declaration->getQualifiedNameAsString(),
487 if (declaration->isTemplated()) {
488 if (declaration->getDescribedTemplate() !=
nullptr) {
492 eid_t{declaration->getDescribedTemplate()->getID()}))
497 std::unique_ptr<model::function> function_model_ptr{};
499 if (declaration->isFunctionTemplateSpecialization()) {
507 if (!function_model_ptr)
510 function_model_ptr->set_id(
513 function_model_ptr->is_void(declaration->getReturnType()->isVoidType());
515 function_model_ptr->is_operator(declaration->isOverloadedOperator());
517 function_model_ptr->is_cuda_kernel(
520 function_model_ptr->is_cuda_device(
527 if (declaration->isThisDeclarationADefinition()) {
529 declaration->getFirstDecl()->getID(), function_model_ptr->id());
532 set_unique_id(declaration->getID(), function_model_ptr->id());
538 diagram().add_participant(std::move(function_model_ptr));
544 clang::FunctionTemplateDecl *declaration)
549 auto context_backup =
context();
551 RecursiveASTVisitor<translation_unit_visitor>::TraverseFunctionTemplateDecl(
560 clang::FunctionTemplateDecl *declaration)
565 const auto function_name = declaration->getQualifiedNameAsString();
567 LOG_TRACE(
"Visiting function template declaration {} at {}", function_name,
577 function_template_model->is_void(
578 declaration->getAsFunction()->getReturnType()->isVoidType());
580 function_template_model->set_id(
583 function_template_model->is_operator(
584 declaration->getAsFunction()->isOverloadedOperator());
590 set_unique_id(declaration->getID(), function_template_model->id());
592 diagram().add_participant(std::move(function_template_model));
602 const auto lambda_full_name =
603 expr->getLambdaClass()->getCanonicalDecl()->getNameAsString();
605 LOG_TRACE(
"Visiting lambda expression {} at {} [caller_id = {}]",
606 lambda_full_name, expr->getBeginLoc().printToString(
source_manager()),
609 LOG_TRACE(
"Lambda call operator ID {} - lambda class ID {}, class call "
611 expr->getCallOperator()->getID(), expr->getLambdaClass()->getID(),
612 expr->getLambdaClass()->getLambdaCallOperator()->getID());
615 auto *cls = expr->getLambdaClass();
618 if (!lambda_class_model_ptr)
621 const auto cls_id = lambda_class_model_ptr->id();
625 auto lambda_method_model_ptr =
628 lambda_method_model_ptr->set_class_id(cls_id);
631 auto lambda_class_full_name = lambda_class_model_ptr->full_name(
false);
632 lambda_method_model_ptr->set_class_full_name(lambda_class_full_name);
634 diagram().add_participant(std::move(lambda_class_model_ptr));
636 lambda_method_model_ptr->set_id(
638 "::" + lambda_method_model_ptr->full_name_no_ns()));
640 get_participant<model::class_>(cls_id).value().set_lambda_operator_id(
641 lambda_method_model_ptr->id());
645 if (std::holds_alternative<clang::CallExpr *>(
646 context().current_callexpr()) &&
647 (!
context().lambda_caller_id().has_value())) {
653 m.set_from(
context().caller_id());
654 m.set_to(lambda_method_model_ptr->id());
658 diagram().add_active_participant(m.from());
659 diagram().add_active_participant(m.to());
661 LOG_DBG(
"Found call {} from {} [{}] to {} [{}]", m.message_name(),
662 m.from(), m.from(), m.to(), m.to());
671 expr->getCallOperator()->getID(), lambda_method_model_ptr->id());
673 diagram().add_participant(std::move(lambda_method_model_ptr));
675 [[maybe_unused]]
const auto is_generic_lambda = expr->isGenericLambda();
682 RecursiveASTVisitor<translation_unit_visitor>::TraverseLambdaExpr(expr);
691 clang::ObjCMessageExpr *expr)
693 if (!
config().include_system_headers() &&
694 source_manager().isInSystemHeader(expr->getSourceRange().getBegin()))
697 LOG_TRACE(
"Entering ObjC message expression at {}",
702 RecursiveASTVisitor<translation_unit_visitor>::TraverseObjCMessageExpr(
705 LOG_TRACE(
"Leaving ObjC message expression at {}",
717 if (!
config().include_system_headers() &&
718 source_manager().isInSystemHeader(expr->getSourceRange().getBegin()))
721 LOG_TRACE(
"Entering call expression at {}",
726 RecursiveASTVisitor<translation_unit_visitor>::TraverseCallExpr(expr);
728 LOG_TRACE(
"Leaving call expression at {}",
739 clang::CUDAKernelCallExpr *expr)
741 if (!
config().include_system_headers() &&
742 source_manager().isInSystemHeader(expr->getSourceRange().getBegin()))
745 LOG_TRACE(
"Entering CUDA kernel call expression at {}",
750 RecursiveASTVisitor<translation_unit_visitor>::TraverseCallExpr(expr);
752 LOG_TRACE(
"Leaving CUDA kernel call expression at {}",
763 clang::CXXMemberCallExpr *expr)
765 if (!
config().include_system_headers() &&
766 source_manager().isInSystemHeader(expr->getSourceRange().getBegin()))
769 LOG_TRACE(
"Entering member call expression at {}",
774 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXMemberCallExpr(
777 LOG_TRACE(
"Leaving member call expression at {}",
788 clang::CXXOperatorCallExpr *expr)
792 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXOperatorCallExpr(
803 clang::CXXTemporaryObjectExpr *expr)
811 clang::dyn_cast<clang::CXXConstructExpr>(expr));
821 clang::CXXConstructExpr *expr)
823 LOG_TRACE(
"Entering cxx construct call expression at {}",
828 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXConstructExpr(
833 LOG_TRACE(
"Leaving cxx construct call expression at {}",
854 const auto *current_elseifstmt =
860 if (current_elseifstmt !=
nullptr) {
861 if (current_elseifstmt->getElse() == stmt) {
864 if (current_caller_id.value() != 0) {
867 diagram().add_message(std::move(m));
871 else if (current_ifstmt !=
nullptr) {
872 if (current_ifstmt->getElse() == stmt) {
875 if (current_caller_id.value() != 0) {
878 diagram().add_message(std::move(m));
883 RecursiveASTVisitor<translation_unit_visitor>::TraverseCompoundStmt(stmt);
895 bool elseif_block{
false};
899 const auto *current_elseifstmt =
902 std::string condition_text;
903 if (
config().generate_condition_statements())
908 auto child_stmt_compare = [stmt](
auto *child_stmt) {
909 return child_stmt == stmt;
912 if (current_ifstmt !=
nullptr)
913 elseif_block = elseif_block ||
914 std::any_of(current_ifstmt->children().begin(),
915 current_ifstmt->children().end(), child_stmt_compare);
916 if (current_elseifstmt !=
nullptr)
917 elseif_block = elseif_block ||
918 std::any_of(current_elseifstmt->children().begin(),
919 current_elseifstmt->children().end(), child_stmt_compare);
921 if ((current_caller_id.value() != 0) && !stmt->isConstexpr()) {
925 message m{message_t::kElseIf, current_caller_id};
927 m.condition_text(condition_text);
929 *
context().get_ast_context(), current_caller_id, stmt));
930 diagram().add_block_message(std::move(m));
937 message m{message_t::kIf, current_caller_id};
939 m.condition_text(condition_text);
941 *
context().get_ast_context(), current_caller_id, stmt));
942 diagram().add_block_message(std::move(m));
946 RecursiveASTVisitor<translation_unit_visitor>::TraverseIfStmt(stmt);
948 if ((current_caller_id.value() != 0) && !stmt->isConstexpr()) {
951 {message_t::kIfEnd, current_caller_id}, message_t::kIf);
967 std::string condition_text;
968 if (
config().generate_condition_statements())
971 if (current_caller_id.value() != 0) {
972 LOG_TRACE(
"Entering while statement at {}",
976 message m{message_t::kWhile, current_caller_id};
978 m.condition_text(condition_text);
980 *
context().get_ast_context(), current_caller_id, stmt));
981 diagram().add_block_message(std::move(m));
984 RecursiveASTVisitor<translation_unit_visitor>::TraverseWhileStmt(stmt);
986 if (current_caller_id.value() != 0) {
988 {message_t::kWhileEnd, current_caller_id}, message_t::kWhile);
1003 std::string condition_text;
1004 if (
config().generate_condition_statements())
1007 if (current_caller_id.value() != 0) {
1009 message m{message_t::kDo, current_caller_id};
1011 m.condition_text(condition_text);
1013 *
context().get_ast_context(), current_caller_id, stmt));
1014 diagram().add_block_message(std::move(m));
1017 RecursiveASTVisitor<translation_unit_visitor>::TraverseDoStmt(stmt);
1019 if (current_caller_id.value() != 0) {
1021 {message_t::kDoEnd, current_caller_id}, message_t::kDo);
1036 std::string condition_text;
1037 if (
config().generate_condition_statements())
1040 if (current_caller_id.value() != 0) {
1042 message m{message_t::kFor, current_caller_id};
1044 m.condition_text(condition_text);
1047 *
context().get_ast_context(), current_caller_id, stmt));
1049 diagram().add_block_message(std::move(m));
1052 RecursiveASTVisitor<translation_unit_visitor>::TraverseForStmt(stmt);
1054 if (current_caller_id.value() != 0) {
1056 {message_t::kForEnd, current_caller_id}, message_t::kFor);
1071 if (current_caller_id.value() != 0) {
1073 message m{message_t::kTry, current_caller_id};
1076 *
context().get_ast_context(), current_caller_id, stmt));
1077 diagram().add_block_message(std::move(m));
1080 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXTryStmt(stmt);
1082 if (current_caller_id.value() != 0) {
1084 {message_t::kTryEnd, current_caller_id}, message_t::kTry);
1099 if ((current_caller_id.value() != 0) &&
1100 (
context().current_trystmt() !=
nullptr)) {
1101 std::string caught_type;
1102 if (stmt->getCaughtType().isNull())
1103 caught_type =
"...";
1106 stmt->getCaughtType(), *
context().get_ast_context());
1110 diagram().add_message(std::move(m));
1113 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXCatchStmt(stmt);
1119 clang::CXXForRangeStmt *stmt)
1127 std::string condition_text;
1128 if (
config().generate_condition_statements())
1131 if (current_caller_id.value() != 0) {
1133 message m{message_t::kFor, current_caller_id};
1135 m.condition_text(condition_text);
1137 *
context().get_ast_context(), current_caller_id, stmt));
1138 diagram().add_block_message(std::move(m));
1141 RecursiveASTVisitor<translation_unit_visitor>::TraverseCXXForRangeStmt(
1144 if (current_caller_id.value() != 0) {
1146 {message_t::kForEnd, current_caller_id}, message_t::kFor);
1159 if (current_caller_id.value() != 0) {
1164 *
context().get_ast_context(), current_caller_id, stmt));
1165 diagram().add_block_message(std::move(m));
1168 RecursiveASTVisitor<translation_unit_visitor>::TraverseSwitchStmt(stmt);
1170 if (current_caller_id.value() != 0) {
1173 {message_t::kSwitchEnd, current_caller_id}, message_t::kSwitch);
1185 if ((current_caller_id.value() != 0) &&
1186 (
context().current_switchstmt() !=
nullptr)) {
1189 diagram().add_case_stmt_message(std::move(m));
1192 RecursiveASTVisitor<translation_unit_visitor>::TraverseCaseStmt(stmt);
1203 if ((current_caller_id.value() != 0) &&
1204 (
context().current_switchstmt() !=
nullptr)) {
1207 diagram().add_case_stmt_message(std::move(m));
1210 RecursiveASTVisitor<translation_unit_visitor>::TraverseDefaultStmt(stmt);
1216 clang::ConditionalOperator *stmt)
1222 std::string condition_text;
1223 if (
config().generate_condition_statements())
1226 if (current_caller_id.value() != 0) {
1230 m.condition_text(condition_text);
1232 *
context().get_ast_context(), current_caller_id, stmt));
1233 diagram().add_block_message(std::move(m));
1236 RecursiveASTVisitor<translation_unit_visitor>::TraverseStmt(
1239 RecursiveASTVisitor<translation_unit_visitor>::TraverseStmt(
1240 stmt->getTrueExpr());
1242 if (current_caller_id.value() != 0) {
1243 model::message m{message_t::kConditionalElse, current_caller_id};
1245 diagram().add_message(std::move(m));
1248 RecursiveASTVisitor<translation_unit_visitor>::TraverseStmt(
1249 stmt->getFalseExpr());
1251 if (current_caller_id.value() != 0) {
1254 {message_t::kConditionalEnd, current_caller_id},
1255 message_t::kConditional);
1262 clang::ObjCMessageExpr *expr)
1270 if (!
context().valid() ||
context().get_ast_context() ==
nullptr)
1273 LOG_TRACE(
"Visiting ObjC message expression at {} [caller_id = {}]",
1284 raw_expr_comment,
context().get_ast_context()->getDiagnostics(), m);
1292 LOG_DBG(
"Skipping call expression due to filter at: {}",
1299 if (
context().is_expr_in_current_control_statement_condition(expr)) {
1303 if (generated_message_from_comment) {
1305 "Message for this call expression is taken from comment directive");
1312 if (m.from().value() > 0 && m.to().value() > 0) {
1313 if (raw_expr_comment !=
nullptr)
1314 m.set_comment(raw_expr_comment->getBeginLoc().getHashValue(),
1319 diagram().add_active_participant(m.from());
1320 diagram().add_active_participant(m.to());
1322 LOG_DBG(
"Found ObjC message {} from {} [{}] to {} [{}] ",
1323 m.message_name(), m.from(), m.from(), m.to(), m.to());
1332 clang::ObjCPropertyRefExpr *expr)
1340 if (!
context().valid() ||
context().get_ast_context() ==
nullptr)
1343 LOG_TRACE(
"Visiting ObjC property ref expression at {} [caller_id = {}]",
1358 if (!
context().valid() ||
context().get_ast_context() ==
nullptr)
1361 LOG_TRACE(
"Visiting call expression at {} [caller_id = {}]",
1374 raw_expr_comment,
context().get_ast_context()->getDiagnostics(), m);
1382 LOG_DBG(
"Skipping call expression due to filter at: {}",
1389 if (
context().is_expr_in_current_control_statement_condition(expr)) {
1393 if (generated_message_from_comment) {
1395 "Message for this call expression is taken from comment directive");
1400 else if (
const auto *cuda_call_expr =
1401 clang::dyn_cast_or_null<clang::CUDAKernelCallExpr>(expr);
1402 cuda_call_expr !=
nullptr) {
1409 else if (
const auto *operator_call_expr =
1410 clang::dyn_cast_or_null<clang::CXXOperatorCallExpr>(expr);
1411 operator_call_expr !=
nullptr) {
1419 else if (
const auto *method_call_expr =
1420 clang::dyn_cast_or_null<clang::CXXMemberCallExpr>(expr);
1421 method_call_expr !=
nullptr) {
1430 auto *callee_decl = expr->getCalleeDecl();
1432 if (callee_decl ==
nullptr) {
1433 LOG_DBG(
"Cannot get callee declaration - trying direct function "
1436 callee_decl = expr->getDirectCallee();
1438 if (callee_decl !=
nullptr)
1439 LOG_DBG(
"Found function/method callee in: {}",
1443 if (callee_decl ==
nullptr) {
1447 if (clang::dyn_cast_or_null<clang::CXXDependentScopeMemberExpr>(
1448 expr->getCallee()) !=
nullptr) {
1457 else if (clang::dyn_cast_or_null<clang::UnresolvedLookupExpr>(
1458 expr->getCallee()) !=
nullptr) {
1462 else if (clang::dyn_cast_or_null<clang::LambdaExpr>(
1463 expr->getCallee()) !=
nullptr) {
1464 LOG_DBG(
"Processing lambda expression callee");
1468 else if (clang::dyn_cast_or_null<clang::DependentScopeDeclRefExpr>(
1469 expr->getCallee()) !=
nullptr) {
1470 LOG_DBG(
"Processing dependent scope declaration expression "
1471 "callee - not able to infer the template parameter "
1472 "type at this point: {}",
1476 LOG_DBG(
"Found unsupported callee decl type for: {} at {}",
1485 LOG_DBG(
"Skipping call expression at: {}",
1494 if (m.from().value() > 0 && m.to().value() > 0) {
1495 if (raw_expr_comment !=
nullptr)
1496 m.set_comment(raw_expr_comment->getBeginLoc().getHashValue(),
1501 diagram().add_active_participant(m.from());
1502 diagram().add_active_participant(m.to());
1504 LOG_DBG(
"Found call {} from {} [{}] to {} [{}] ", m.message_name(),
1505 m.from(), m.from(), m.to(), m.to());
1517 diagram().sequences().insert({m.
from(), std::move(a)});
1527 auto generated_message_from_comment{
false};
1528 for (
const auto &decorator : m.
decorators()) {
1529 auto call_decorator =
1530 std::dynamic_pointer_cast<decorators::call>(decorator);
1531 if (call_decorator &&
1532 call_decorator->applies_to_diagram(
config().name)) {
1534 generated_message_from_comment =
true;
1538 return generated_message_from_comment;
1543 if (decl->isStaticLocal())
1546 RecursiveASTVisitor::TraverseVarDecl(decl);
1548 if (decl->isStaticLocal())
1555 clang::CXXConstructExpr *expr)
1563 if (expr ==
nullptr)
1566 if (
const auto *ctor = expr->getConstructor();
1570 LOG_TRACE(
"Visiting cxx construct expression at {} [caller_id = {}]",
1580 if (
context().is_expr_in_current_control_statement_condition(expr)) {
1587 if (m.from().value() > 0 && m.to().value() > 0) {
1590 diagram().add_active_participant(m.from());
1591 diagram().add_active_participant(m.to());
1593 LOG_DBG(
"Found constructor call {} from {} [{}] to {} [{}] ",
1594 m.message_name(), m.from(), m.from(), m.to(), m.to());
1605 const auto *callee_decl = expr->getCalleeDecl();
1607 if (callee_decl ==
nullptr)
1610 const auto *callee_function = callee_decl->getAsFunction();
1612 if (callee_function ==
nullptr)
1619 if (
config().combine_free_functions_into_file_participants() &&
1623 auto callee_name = callee_function->getQualifiedNameAsString() +
"()";
1632 model::message &m,
const clang::CXXOperatorCallExpr *operator_call_expr)
1634 if (operator_call_expr->getCalleeDecl() ==
nullptr)
1637 LOG_DBG(
"Operator '{}' call expression to {} at {}",
1638 getOperatorSpelling(operator_call_expr->getOperator()),
1639 operator_call_expr->getCalleeDecl()->getID(),
1640 operator_call_expr->getBeginLoc().printToString(
source_manager()));
1643 if (
const auto *lambda_method = clang::dyn_cast<clang::CXXMethodDecl>(
1644 operator_call_expr->getCalleeDecl());
1645 lambda_method !=
nullptr && lambda_method->getParent()->isLambda()) {
1647 LOG_DBG(
"Operator callee is a lambda: {}",
1650 const auto source_location{
1655 m.
set_to(
eid_t{lambda_method->getParent()->getID()});
1658 const auto operator_ast_id =
1659 operator_call_expr->getCalleeDecl()->getID();
1664 "operator{}", getOperatorSpelling(operator_call_expr->getOperator())));
1670 model::message &m,
const clang::CXXConstructExpr *construct_expr)
1672 const auto *constructor = construct_expr->getConstructor();
1673 if (constructor ==
nullptr)
1676 const auto *constructor_parent = constructor->getParent();
1677 if (constructor_parent ==
nullptr)
1680 LOG_DBG(
"Constructor '{}' call expression to {} at {}",
1681 construct_expr->getConstructor()->getNameAsString(),
1682 constructor->getID(),
1687 fmt::format(
"{}::{}", constructor_parent->getQualifiedNameAsString(),
1688 constructor_parent->getNameAsString()));
1690 diagram().add_active_participant(
eid_t{constructor->getID()});
1698 const auto *method_decl = message_expr->getMethodDecl();
1700 if (method_decl ==
nullptr)
1703 std::string method_name = method_decl->getQualifiedNameAsString();
1705 if (message_expr->getReceiverInterface() ==
nullptr)
1708 const auto *callee_decl = message_expr->getReceiverInterface();
1710 if (callee_decl ==
nullptr)
1716 if (callee_decl->getImplementation() !=
nullptr &&
1717 callee_decl->getImplementation()->getMethod(method_decl->getSelector(),
1718 method_decl->isInstanceMethod(),
true) !=
nullptr) {
1719 const auto *impl_method_decl =
1720 callee_decl->getImplementation()->getMethod(
1721 method_decl->getSelector(), method_decl->isInstanceMethod(),
1731 message_expr->getCallReturnType(*
context().get_ast_context())
1734 LOG_TRACE(
"Set callee ObjC method id {} for method name {}", m.
to(),
1735 method_decl->getQualifiedNameAsString());
1737 diagram().add_active_participant(
eid_t{method_decl->getID()});
1743 model::message &m,
const clang::CXXMemberCallExpr *method_call_expr)
1746 const auto *method_decl = method_call_expr->getMethodDecl();
1748 if (method_decl ==
nullptr)
1751 std::string method_name = method_decl->getQualifiedNameAsString();
1753 const auto *callee_decl =
1754 method_decl !=
nullptr ? method_decl->getParent() :
nullptr;
1756 if (callee_decl ==
nullptr)
1765 method_call_expr->getCallReturnType(*
context().get_ast_context())
1768 LOG_TRACE(
"Set callee method id {} for method name {}", m.
to(),
1769 method_decl->getQualifiedNameAsString());
1771 diagram().add_active_participant(
eid_t{method_decl->getID()});
1779 const auto *dependent_member_callee =
1780 clang::dyn_cast_or_null<clang::CXXDependentScopeMemberExpr>(
1783 if (dependent_member_callee ==
nullptr)
1787 if (
const auto *tst = dependent_member_callee->getBaseType()
1788 ->getAs<clang::TemplateSpecializationType>();
1790 const auto *template_declaration =
1791 tst->getTemplateName().getAsTemplateDecl();
1793 std::string callee_method_full_name;
1801 "::" + dependent_member_callee->getMember().getAsString();
1803 for (
const auto &[
id, p] :
diagram().participants()) {
1804 const auto p_full_name = p->full_name(
false);
1806 if (p_full_name.find(callee_method_full_name +
"(") == 0) {
1816 const auto *argument_template =
1817 template_declaration->getTemplateParameters()
1826 dependent_member_callee->getMember().getAsString();
1828 for (
const auto &[
id, p] :
diagram().participants()) {
1829 const auto p_full_name = p->full_name(
false);
1830 if (p_full_name.find(callee_method_full_name +
"(") ==
1844 dependent_member_callee->getMember().getAsString());
1846 if (
const auto maybe_id =
1848 maybe_id.has_value())
1849 diagram().add_active_participant(maybe_id.value());
1853 LOG_DBG(
"Skipping call due to unresolvable "
1854 "CXXDependentScopeMemberExpr at {}",
1864 const auto *callee_decl = expr->getCalleeDecl();
1866 if (callee_decl ==
nullptr)
1869 const auto *callee_function = callee_decl->getAsFunction();
1871 if (callee_function ==
nullptr)
1878 if (
config().combine_free_functions_into_file_participants() &&
1882 auto callee_name = callee_function->getQualifiedNameAsString() +
"()";
1893 const auto *lambda_expr =
1894 clang::dyn_cast_or_null<clang::LambdaExpr>(expr->getCallee());
1896 if (lambda_expr ==
nullptr)
1899 const auto lambda_class_id =
eid_t{lambda_expr->getLambdaClass()->getID()};
1909 const auto *unresolved_expr =
1910 clang::dyn_cast_or_null<clang::UnresolvedLookupExpr>(expr->getCallee());
1912 if (unresolved_expr !=
nullptr) {
1913 for (
const auto *decl : unresolved_expr->decls()) {
1914 if (clang::dyn_cast_or_null<clang::FunctionTemplateDecl>(decl) !=
1917 clang::dyn_cast_or_null<clang::FunctionTemplateDecl>(decl);
1922 if (clang::dyn_cast_or_null<clang::FunctionDecl>(decl) !=
nullptr) {
1924 clang::dyn_cast_or_null<clang::FunctionDecl>(decl);
1929 LOG_DBG(
"Unknown unresolved lookup expression");
1937 const clang::CXXDependentScopeMemberExpr *dependent_member_expr)
const
1939 if (dependent_member_expr ==
nullptr)
1942 if (dependent_member_expr->getBaseType().isNull())
1945 const auto *tst = dependent_member_expr->getBaseType()
1946 ->getAs<clang::TemplateSpecializationType>();
1951 return !(tst->isPointerType());
1955 const clang::TemplateDecl *primary_template)
const
1957 return primary_template->getQualifiedNameAsString().find(
1958 "std::unique_ptr") == 0 ||
1959 primary_template->getQualifiedNameAsString().find(
"std::shared_ptr") ==
1961 primary_template->getQualifiedNameAsString().find(
"std::weak_ptr") == 0;
1964std::unique_ptr<clanguml::sequence_diagram::model::class_>
1966 clang::ObjCProtocolDecl *cls)
1968 assert(cls !=
nullptr);
1970 auto c_ptr{std::make_unique<clanguml::sequence_diagram::model::class_>(
1971 config().using_namespace())};
1974 auto qualified_name = cls->getQualifiedNameAsString();
1976 c.is_objc_interface();
1978 c.set_name(cls->getQualifiedNameAsString());
1987 c.set_style(c.style_spec());
1992std::unique_ptr<clanguml::sequence_diagram::model::class_>
1994 clang::ObjCInterfaceDecl *cls)
1996 assert(cls !=
nullptr);
1998 auto c_ptr{std::make_unique<clanguml::sequence_diagram::model::class_>(
1999 config().using_namespace())};
2002 auto qualified_name = cls->getQualifiedNameAsString();
2004 c.is_objc_interface(
true);
2006 c.set_name(cls->getQualifiedNameAsString());
2015 c.set_style(c.style_spec());
2020std::unique_ptr<clanguml::sequence_diagram::model::class_>
2023 assert(cls !=
nullptr);
2025 auto c_ptr{std::make_unique<clanguml::sequence_diagram::model::class_>(
2026 config().using_namespace())};
2029 auto qualified_name = cls->getQualifiedNameAsString();
2031 if (!cls->isLambda())
2040 const auto *parent = cls->getParent();
2042 if ((parent !=
nullptr) && parent->isRecord()) {
2047 std::optional<eid_t> id_opt;
2048 const auto *parent_record_decl =
2049 clang::dyn_cast<clang::RecordDecl>(parent);
2051 assert(parent_record_decl !=
nullptr);
2053 const eid_t ast_id{parent_record_decl->getID()};
2061 (parent_record_decl->getDescribedTemplate() !=
nullptr)) {
2062 parent_record_decl->getDescribedTemplate()->getID();
2063 if (parent_record_decl->getDescribedTemplate() !=
nullptr)
2075 if (!parent_class) {
2080 if (cls->getNameAsString().empty()) {
2083 const auto &[label, hint, access] =
2086 c.set_name(parent_class.value().name() +
2087 "::" + fmt::format(
"({})", label));
2089 parent_class.value().add_relationship(
2093 c.set_name(parent_class.value().name() +
"::" +
2095 "(anonymous_{})", std::to_string(cls->getID())));
2099 parent_class.value().name() +
"::" + cls->getNameAsString());
2106 else if (cls->isLambda()) {
2108 if (cls->getParent() !=
nullptr) {
2111 c.set_name(type_name);
2112 c.set_namespace(ns);
2116 LOG_WARN(
"Cannot find parent declaration for lambda {}",
2117 cls->getQualifiedNameAsString());
2123 c.set_namespace(ns);
2127 c.is_struct(cls->isStruct());
2135 c.set_style(c.style_spec());
2142 LOG_TRACE(
"Setting local element mapping {} --> {}", local_id, global_id);
2150 eid_t local_id)
const
2158std::unique_ptr<model::function_template>
2160 const clang::FunctionTemplateDecl &declaration)
2162 auto function_template_model_ptr =
2163 std::make_unique<sequence_diagram::model::function_template>(
2164 config().using_namespace());
2169 *function_template_model_ptr, declaration);
2171 function_template_model_ptr->return_type(
2173 declaration.getASTContext()));
2175 for (
const auto *param : declaration.getTemplatedDecl()->parameters()) {
2176 function_template_model_ptr->add_parameter(
2178 param->getType(), declaration.getASTContext(),
false)));
2181 return function_template_model_ptr;
2184std::unique_ptr<model::function_template>
2186 const clang::FunctionDecl &decl)
2188 auto template_instantiation_ptr =
2189 std::make_unique<model::function_template>(
config().using_namespace());
2190 auto &template_instantiation = *template_instantiation_ptr;
2194 tbuilder().
build(template_instantiation, &decl, decl.getPrimaryTemplate(),
2195 decl.getTemplateSpecializationArgs()->asArray(),
2199 for (
const auto *param : decl.parameters()) {
2200 template_instantiation_ptr->add_parameter(
2204 return template_instantiation_ptr;
2208 const clang::FunctionDecl &declaration)
2210 auto function_model_ptr =
2211 std::make_unique<sequence_diagram::model::function>(
2212 config().using_namespace());
2215 function_model_ptr->set_name(ns.name());
2217 function_model_ptr->set_namespace(ns);
2220 declaration.getReturnType(), declaration.getASTContext()));
2222 for (
const auto *param : declaration.parameters()) {
2223 function_model_ptr->add_parameter(
2225 param->getType(), declaration.getASTContext(),
false)));
2228 if (declaration.isVariadic()) {
2229 function_model_ptr->add_parameter(
"...");
2232 return function_model_ptr;
2235std::unique_ptr<model::class_>
2237 clang::ClassTemplateSpecializationDecl *cls)
2239 auto c_ptr{std::make_unique<model::class_>(
config().using_namespace())};
2243 auto &template_instantiation = *c_ptr;
2246 auto qualified_name = cls->getQualifiedNameAsString();
2252 template_instantiation.set_name(cls->getNameAsString());
2253 template_instantiation.set_namespace(ns);
2255 template_instantiation.is_struct(cls->isStruct());
2261 if (template_instantiation.skip())
2264 template_instantiation.set_id(
2273 const std::string &full_name)
const
2275 return config().simplify_template_type(full_name);
2279 const clang::SourceLocation &source_location)
const
2281 const auto file_line =
2283 const auto file_column =
2285 const std::string file_name =
2287 .make_path_relative(
2290 return fmt::format(
"{}:{}:{}", file_name, file_line, file_column);
2294 const clang::CXXRecordDecl *cls)
const
2297 const auto location = cls->getLocation();
2301 if (maybe_lambda_caller_id.has_value()) {
2303 std::string parent_lambda_class_name{
"()"};
2304 if (
diagram().get_participant<model::method>(
2305 maybe_lambda_caller_id.value())) {
2306 auto parent_lambda_class_id =
2309 maybe_lambda_caller_id.value())
2314 parent_lambda_class_id)) {
2315 parent_lambda_class_name =
2323 result = fmt::format(
2324 "{}##(lambda {})", parent_lambda_class_name, source_location);
2326 else if (
context().caller_id().value() != 0 &&
2328 auto parent_full_name =
2332 fmt::format(
"{}##(lambda {})", parent_full_name, source_location);
2335 result = fmt::format(
"(lambda {})", source_location);
2361 assert(expr !=
nullptr);
2374 auto caller_id = msg.from();
2379 if (
diagram().has_activity(caller_id))
2380 diagram().get_activity(caller_id).add_message(std::move(msg));
2382 LOG_DBG(
"Skipping message due to missing activity: {}", caller_id);
2391 clang::CXXConstructExpr *expr)
2393 assert(expr !=
nullptr);
2403 auto caller_id = msg.from();
2404 diagram().get_activity(caller_id).add_message(std::move(msg));
2410 clang::ObjCMessageExpr *expr)
2412 assert(expr !=
nullptr);
2421 auto caller_id = msg.from();
2422 diagram().get_activity(caller_id).add_message(std::move(msg));
2439 if (
config().inline_lambda_messages())
2440 diagram().inline_lambda_operator_calls();
2445 for (
auto &[
id, activity] :
diagram().sequences()) {
2446 for (
auto &m : activity.messages()) {
2449 if (participant && participant.value().is_lambda() &&
2450 participant.value().lambda_operator_id().value() != 0) {
2451 LOG_DBG(
"Changing lambda expression target id from {} to {}",
2452 m.to(), participant.value().lambda_operator_id());
2454 m.set_to(participant.value().lambda_operator_id());
2455 m.set_message_name(
"operator()");
2465 std::set<eid_t> active_participants_unique;
2468 for (
auto id :
diagram().active_participants()) {
2470 !
id.is_global() && unique_id.has_value()) {
2471 active_participants_unique.emplace(unique_id.value());
2473 else if (
id.is_global()) {
2474 active_participants_unique.emplace(
id);
2478 diagram().active_participants() = std::move(active_participants_unique);
2481 for (
auto &[
id, activity] :
diagram().sequences()) {
2482 for (
auto &m : activity.messages()) {
2484 !m.to().is_global() && unique_id.has_value()) {
2485 m.set_to(unique_id.value());
2486 assert(m.to().is_global());
2495 std::map<eid_t, std::set<eid_t>> acs;
2500 std::set<eid_t> unique_caller_ids;
2501 for (
const auto &caller_id : caller_ids) {
2503 if (unique_caller_id)
2504 unique_caller_ids.emplace(*unique_caller_id);
2506 acs.emplace(*unique_id, std::move(unique_caller_ids));
2510 for (
auto &[
id, activity] :
diagram().sequences()) {
2511 assert(
id.is_global());
2513 if (acs.count(
id) > 0) {
2514 activity.set_callers(acs.at(
id));
2519std::unique_ptr<clanguml::sequence_diagram::model::method>
2521 clang::CXXMethodDecl *declaration)
2523 auto method_model_ptr = std::make_unique<sequence_diagram::model::method>(
2524 config().using_namespace());
2527 auto method_name = ns.
name();
2528 method_model_ptr->set_method_name(method_name);
2530 method_model_ptr->set_name(ns.name());
2532 method_model_ptr->set_namespace(ns);
2534 method_model_ptr->is_defaulted(declaration->isDefaulted());
2535 method_model_ptr->is_assignment(declaration->isCopyAssignmentOperator() ||
2536 declaration->isMoveAssignmentOperator());
2537 method_model_ptr->is_const(declaration->isConst());
2538 method_model_ptr->is_static(declaration->isStatic());
2539 method_model_ptr->is_operator(declaration->isOverloadedOperator());
2540 method_model_ptr->is_constructor(
2541 clang::dyn_cast<clang::CXXConstructorDecl>(declaration) !=
nullptr);
2543 method_model_ptr->is_void(declaration->getReturnType()->isVoidType());
2546 declaration->getReturnType(), declaration->getASTContext()));
2548 for (
const auto *param : declaration->parameters()) {
2549 auto parameter_type =
2553 method_model_ptr->add_parameter(
config().using_namespace().relative(
2557 return method_model_ptr;
2560std::unique_ptr<clanguml::sequence_diagram::model::objc_method>
2562 clang::ObjCMethodDecl *declaration)
2564 auto method_model_ptr =
2565 std::make_unique<sequence_diagram::model::objc_method>(
2566 config().using_namespace());
2569 auto method_name = ns.
name();
2570 method_model_ptr->set_method_name(method_name);
2572 method_model_ptr->set_name(ns.name());
2573 method_model_ptr->set_namespace({});
2575 clang::Decl *parent_decl = declaration->getClassInterface();
2577 if (parent_decl ==
nullptr) {
2578 LOG_DBG(
"Cannot find ObjC interface for method, probably it is a "
2585 LOG_DBG(
"Getting ObjC method's interface with local id {}",
2586 parent_decl->getID());
2588 const auto maybe_method_class = get_participant<model::class_>(parent_decl);
2590 if (!maybe_method_class) {
2591 LOG_DBG(
"Cannot find parent class_ for method {} in class {}",
2592 declaration->getQualifiedNameAsString(),
2593 declaration->getClassInterface()->getQualifiedNameAsString());
2597 const auto &method_class = maybe_method_class.value();
2599 method_model_ptr->is_void(declaration->getReturnType()->isVoidType());
2601 method_model_ptr->set_class_id(method_class.id());
2602 method_model_ptr->set_class_full_name(method_class.full_name(
false));
2603 method_model_ptr->set_name(
get_participant(method_model_ptr->class_id())
2605 .full_name_no_ns() +
2606 "::" + declaration->getNameAsString());
2609 declaration->getReturnType(), declaration->getASTContext()));
2611 for (
const auto *param : declaration->parameters()) {
2612 auto parameter_type =
2616 method_model_ptr->add_parameter(parameter_type);
2619 return method_model_ptr;
2622std::unique_ptr<clanguml::sequence_diagram::model::method>
2625 auto method_model_ptr = std::make_unique<sequence_diagram::model::method>(
2626 config().using_namespace());
2629 auto method_name = ns.
name();
2630 method_model_ptr->set_method_name(method_name);
2632 method_model_ptr->set_name(ns.name());
2634 method_model_ptr->set_namespace(ns);
2636 method_model_ptr->is_defaulted(declaration->isDefaulted());
2637 method_model_ptr->is_assignment(declaration->isCopyAssignmentOperator() ||
2638 declaration->isMoveAssignmentOperator());
2639 method_model_ptr->is_const(declaration->isConst());
2640 method_model_ptr->is_static(declaration->isStatic());
2641 method_model_ptr->is_operator(declaration->isOverloadedOperator());
2642 method_model_ptr->is_constructor(
2643 clang::dyn_cast<clang::CXXConstructorDecl>(declaration) !=
nullptr);
2645 clang::Decl *parent_decl = declaration->getParent();
2647 if (
context().current_class_template_decl_ !=
nullptr)
2650 LOG_DBG(
"Getting method's class with local id {}", parent_decl->getID());
2652 const auto maybe_method_class = get_participant<model::class_>(parent_decl);
2654 if (!maybe_method_class) {
2655 LOG_DBG(
"Cannot find parent class_ for method {} in class {}",
2656 declaration->getQualifiedNameAsString(),
2657 declaration->getParent()->getQualifiedNameAsString());
2661 const auto &method_class = maybe_method_class.value();
2663 method_model_ptr->is_void(declaration->getReturnType()->isVoidType());
2665 method_model_ptr->set_class_id(method_class.id());
2666 method_model_ptr->set_class_full_name(method_class.full_name(
false));
2667 method_model_ptr->set_name(
get_participant(method_model_ptr->class_id())
2669 .full_name_no_ns() +
2670 "::" + declaration->getNameAsString());
2673 declaration->getReturnType(), declaration->getASTContext()));
2675 for (
const auto *param : declaration->parameters()) {
2676 auto parameter_type =
2680 method_model_ptr->add_parameter(
config().using_namespace().relative(
2684 return method_model_ptr;
2690 dynamic_cast<const clang::NamedDecl *
>(decl));
2694 const clang::ObjCContainerDecl *decl)
const
2697 dynamic_cast<const clang::NamedDecl *
>(decl));
2701 const clang::LambdaExpr *expr)
const
2703 if (
context().caller_id() == 0)
2709 const auto expr_file = expr->getBeginLoc().printToString(
source_manager());
2719 const clang::ObjCMessageExpr *expr)
const
2721 if (
context().caller_id() == 0)
2727 const auto expr_file = expr->getBeginLoc().printToString(
source_manager());
2738 if (
context().caller_id() == 0)
2742 if (expr->isCallToStdMove())
2745 if (expr->isImplicitCXXThis())
2748 if (clang::dyn_cast_or_null<clang::ImplicitCastExpr>(expr) !=
nullptr)
2754 const auto expr_file = expr->getBeginLoc().printToString(
source_manager());
2760 const auto *callee_decl = expr->getCalleeDecl();
2762 if (callee_decl !=
nullptr) {
2763 const auto *callee_function = callee_decl->getAsFunction();
2765 if ((callee_function ==
nullptr) || !
should_include(callee_function)) {
2766 LOG_DBG(
"Skipping call expression at {}",
2778 const clang::CXXMethodDecl *decl)
const
2787 LOG_DBG(
"Including method {}", decl->getQualifiedNameAsString());
2793 const clang::ObjCMethodDecl *decl)
const
2799 LOG_DBG(
"Including ObjC method {}", decl->getQualifiedNameAsString());
2805 const clang::FunctionDecl *decl)
const
2811 const clang::FunctionTemplateDecl *decl)
const
2817 const clang::ClassTemplateDecl *decl)
const
2822std::optional<std::pair<unsigned int, std::string>>
2824 const clang::ASTContext &context,
const eid_t caller_id,
2825 const clang::Stmt *stmt)
2827 const auto *raw_comment =
2830 if (raw_comment ==
nullptr)
2841 raw_comment->getFormattedText(sm, sm.getDiagnostics()));
2843 if (stripped_comment.empty())
2846 return {{raw_comment->getBeginLoc().getHashValue(), stripped_comment}};