diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index c85cc445..14709b29 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -171,7 +171,11 @@ bool translation_unit_visitor::VisitEnumDecl(clang::EnumDecl *enm) } } - assert(id_opt); + if(!id_opt) { + LOG_WARN("Unknown parent for enum {}", qualified_name); + return true; + + } auto parent_class = diagram_.get_class(*id_opt); diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index df2233f3..8c07f3bf 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -265,12 +265,16 @@ bool translation_unit_visitor::VisitClassTemplateSpecializationDecl( bool translation_unit_visitor::VisitCXXMethodDecl(clang::CXXMethodDecl *m) { - if (context().current_class_decl_ == nullptr && - context().current_class_template_decl_ == nullptr && - context().current_class_template_specialization_decl_ == nullptr) + if (!diagram().should_include(m->getParent()->getQualifiedNameAsString())) return true; - LOG_DBG("= Processing method {} in class {} [{}]", + if (!m->isThisDeclarationADefinition()) { + if (m->getDefinition()) + return VisitCXXMethodDecl( + static_cast(m->getDefinition())); + } + + LOG_DBG("Visiting method {} in class {} [{}]", m->getQualifiedNameAsString(), m->getParent()->getQualifiedNameAsString(), (void *)m->getParent()); @@ -293,6 +297,13 @@ bool translation_unit_visitor::VisitCXXMethodDecl(clang::CXXMethodDecl *m) LOG_DBG("Getting method's class with local id {}", parent_decl->getID()); + if (!get_participant(parent_decl)) { + LOG_WARN("Cannot find parent class_ for method {} in class {}", + m->getQualifiedNameAsString(), + m->getParent()->getQualifiedNameAsString()); + return true; + } + const auto &method_class = get_participant(parent_decl).value(); @@ -336,6 +347,12 @@ bool translation_unit_visitor::VisitFunctionDecl(clang::FunctionDecl *f) if (!diagram().should_include(function_name)) return true; + if (!f->isThisDeclarationADefinition()) { + if (f->getDefinition()) + return VisitFunctionDecl( + static_cast(f->getDefinition())); + } + LOG_DBG("Visiting function declaration {} at {}", function_name, f->getLocation().printToString(source_manager())); @@ -497,6 +514,21 @@ bool translation_unit_visitor::TraverseLambdaExpr(clang::LambdaExpr *expr) return true; } +// +// bool translation_unit_visitor::TraverseCompoundStmt(clang::CompoundStmt +// *stmt) { +// const auto lambda_full_name = +// stmt-> +// +// RecursiveASTVisitor::TraverseCompoundStmt(stmt); +// +// LOG_DBG("Leaving lambda expression {} at {}", lambda_full_name, +// expr->getBeginLoc().printToString(source_manager())); +// +// context().leave_lambda_expression(); +// +// return true; +//} bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr) { @@ -505,6 +537,18 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr) using clanguml::sequence_diagram::model::activity; using clanguml::sequence_diagram::model::message; + if (context().caller_id() == 0) + return true; + + LOG_DBG("Visiting call expression at {} [caller_id = {}]", + expr->getBeginLoc().printToString(source_manager()), + context().caller_id()); + + if (context().caller_id() == 2166770483948966160) { + LOG_WARN(">>>>>>> VISITING CALL EXPRESSION IN METHOD " + "one::s3::S3Server::listBuckets()"); + } + // Skip casts, moves and such if (expr->isCallToStdMove()) return true; @@ -528,9 +572,6 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr) m.from = context().lambda_caller_id(); } - LOG_DBG("Visiting call expression at {}", - expr->getBeginLoc().printToString(source_manager())); - if (const auto *operator_call_expr = clang::dyn_cast_or_null(expr); operator_call_expr != nullptr) { @@ -618,6 +659,9 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr) bool translation_unit_visitor::process_operator_call_expression( model::message &m, const clang::CXXOperatorCallExpr *operator_call_expr) { + if (operator_call_expr->getCalleeDecl() == nullptr) + return false; + LOG_DBG("Operator '{}' call expression to {} at {}", getOperatorSpelling(operator_call_expr->getOperator()), operator_call_expr->getCalleeDecl()->getID(), @@ -642,6 +686,10 @@ bool translation_unit_visitor::process_class_method_call_expression( { // Get callee declaration as methods parent const auto *method_decl = method_call_expr->getMethodDecl(); + + if (method_decl == nullptr) + return false; + std::string method_name = method_decl->getQualifiedNameAsString(); auto *callee_decl = method_decl ? method_decl->getParent() : nullptr; @@ -701,12 +749,15 @@ bool translation_unit_visitor::process_class_template_method_call_expression( } } } + // Otherwise check if it is a smart pointer else if (is_smart_pointer(template_declaration)) { - // Otherwise check if it is a smart pointer - template_declaration->getTemplateParameters()->asArray().front(); + const auto *argument_template = + template_declaration->getTemplateParameters() + ->asArray() + .front(); - if (get_participant(template_declaration).has_value()) { - callee_method_full_name = get_participant(template_declaration) + if (get_participant(argument_template).has_value()) { + callee_method_full_name = get_participant(argument_template) .value() .full_name(false) + "::" + dependent_member_callee->getMember().getAsString(); @@ -1657,7 +1708,7 @@ std::string translation_unit_visitor::simplify_system_template( const std::string &full_name) const { std::string result{full_name}; - for(const auto& [k, v] : config().type_aliases()) { + for (const auto &[k, v] : config().type_aliases()) { util::replace_all(result, k, v); } diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.h b/src/sequence_diagram/visitor/translation_unit_visitor.h index a14c0f35..234e1bdf 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.h +++ b/src/sequence_diagram/visitor/translation_unit_visitor.h @@ -50,6 +50,8 @@ public: bool VisitCXXMethodDecl(clang::CXXMethodDecl *method); +// bool TraverseCompoundStmt(clang::CompoundStmt *stmt); + bool VisitCXXRecordDecl(clang::CXXRecordDecl *cls); bool VisitClassTemplateDecl(clang::ClassTemplateDecl *cls); @@ -62,6 +64,8 @@ public: bool VisitFunctionTemplateDecl( clang::FunctionTemplateDecl *function_declaration); + + clanguml::sequence_diagram::model::diagram &diagram(); const clanguml::sequence_diagram::model::diagram &diagram() const; diff --git a/tests/t00048/.clang-uml b/tests/t00048/.clang-uml index 2c8a4cec..7236ae46 100644 --- a/tests/t00048/.clang-uml +++ b/tests/t00048/.clang-uml @@ -6,6 +6,7 @@ diagrams: glob: - ../../tests/t00048/b_t00048.cc - ../../tests/t00048/a_t00048.cc + - ../../tests/t00048/t00048.cc using_namespace: clanguml::t00048 parse_includes: true include: diff --git a/tests/t20014/.clang-uml b/tests/t20014/.clang-uml new file mode 100644 index 00000000..9de444e7 --- /dev/null +++ b/tests/t20014/.clang-uml @@ -0,0 +1,17 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t20014_sequence: + type: sequence + glob: + - ../../tests/t20014/t20014.cc + - ../../tests/t20014/t20014_c.cc + - ../../tests/t20014/t20014_b.cc + - ../../tests/t20014/t20014_a.cc + include: + namespaces: + - clanguml::t20014 + using_namespace: + - clanguml::t20014 + start_from: + - function: "clanguml::t20014::tmain()" \ No newline at end of file diff --git a/tests/t20014/include/t20014.h b/tests/t20014/include/t20014.h new file mode 100644 index 00000000..48b974d5 --- /dev/null +++ b/tests/t20014/include/t20014.h @@ -0,0 +1,9 @@ +#pragma once + +namespace clanguml { +namespace t20014 { + +int tmain(); + +} +} \ No newline at end of file diff --git a/tests/t20014/include/t20014_a.h b/tests/t20014/include/t20014_a.h new file mode 100644 index 00000000..44865287 --- /dev/null +++ b/tests/t20014/include/t20014_a.h @@ -0,0 +1,12 @@ +#pragma once + +namespace clanguml { +namespace t20014 { + +struct A { + int a1(int i, int j); + int a2(int i, int j); +}; + +} +} \ No newline at end of file diff --git a/tests/t20014/include/t20014_b.h b/tests/t20014/include/t20014_b.h new file mode 100644 index 00000000..e41e43c5 --- /dev/null +++ b/tests/t20014/include/t20014_b.h @@ -0,0 +1,16 @@ +#pragma once + +#include "t20014_a.h" + +namespace clanguml { +namespace t20014 { + +struct B { + int b1(int i, int); + int b2(int i, int); + + A a_; +}; + +} +} \ No newline at end of file diff --git a/tests/t20014/include/t20014_c.h b/tests/t20014/include/t20014_c.h new file mode 100644 index 00000000..83f13875 --- /dev/null +++ b/tests/t20014/include/t20014_c.h @@ -0,0 +1,19 @@ +#pragma once + +namespace clanguml { +namespace t20014 { + +template struct C { + F c1(F i, F j) { + return c_.b1(i, j); + } + + F c2(F i, F j){ + return c_.b2(i, j); + } + + T c_; +}; + +} +} \ No newline at end of file diff --git a/tests/t20014/t20014.cc b/tests/t20014/t20014.cc new file mode 100644 index 00000000..c22f030d --- /dev/null +++ b/tests/t20014/t20014.cc @@ -0,0 +1,23 @@ +#include "include/t20014.h" +#include "include/t20014_b.h" +#include "include/t20014_c.h" + +namespace clanguml { +namespace t20014 { + +void log(const char *msg) { } + +int tmain() +{ + B b; + C c; + + b.b1(0, 1); + b.b2(1, 2); + + c.c1(2, 3); + + return 0; +} +} +} \ No newline at end of file diff --git a/tests/t20014/t20014_a.cc b/tests/t20014/t20014_a.cc new file mode 100644 index 00000000..58d2b6bf --- /dev/null +++ b/tests/t20014/t20014_a.cc @@ -0,0 +1,10 @@ +#include "include/t20014_a.h" +namespace clanguml { +namespace t20014 { + +int A::a1(int i, int j) { return i + j; } + +int A::a2(int i, int j) { return i - j; } + +} +} \ No newline at end of file diff --git a/tests/t20014/t20014_b.cc b/tests/t20014/t20014_b.cc new file mode 100644 index 00000000..71fe96e6 --- /dev/null +++ b/tests/t20014/t20014_b.cc @@ -0,0 +1,10 @@ +#include "include/t20014_b.h" +namespace clanguml { +namespace t20014 { + +int B::b1(int i, int j) { return a_.a1(i, j); } + +int B::b2(int i, int j) { return a_.a2(i, j); } + +} +} \ No newline at end of file diff --git a/tests/t20014/t20014_c.cc b/tests/t20014/t20014_c.cc new file mode 100644 index 00000000..363fc63d --- /dev/null +++ b/tests/t20014/t20014_c.cc @@ -0,0 +1,17 @@ +#include "include/t20014_c.h" + +namespace clanguml { +namespace t20014 { + +//template F C::c1(F i, F j) +//{ +// return c_.b1(i, j); +//} +// +//template F C::c2(F i, F j) +//{ +// return c_.b2(i, j); +//} + +} +} \ No newline at end of file diff --git a/tests/t20014/test_case.h b/tests/t20014/test_case.h new file mode 100644 index 00000000..eec0fe57 --- /dev/null +++ b/tests/t20014/test_case.h @@ -0,0 +1,49 @@ +/** + * tests/t20014/test_case.h + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +TEST_CASE("t20014", "[test-case][sequence]") +{ + auto [config, db] = load_config("t20014"); + + auto diagram = config.diagrams["t20014_sequence"]; + + REQUIRE(diagram->name == "t20014_sequence"); + + auto model = generate_sequence_diagram(*db, diagram); + + REQUIRE(model->name() == "t20014_sequence"); + + auto puml = generate_sequence_puml(diagram, *model); + AliasMatcher _A(puml); + + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); + + // Check if all calls exist + REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B"), "b1(int,int)")); + REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "a1(int,int)")); + + REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B"), "b2(int,int)")); + REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "a2(int,int)")); + + REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("C"), "c1(int,int)")); + REQUIRE_THAT(puml, HasCall(_A("C"), _A("B"), "b1(int,int)")); + + save_puml( + "./" + config.output_directory() + "/" + diagram->name + ".puml", puml); +} \ No newline at end of file diff --git a/tests/test_cases.cc b/tests/test_cases.cc index beb24b90..9e0486ea 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -260,6 +260,7 @@ using namespace clanguml::test::matchers; #include "t20011/test_case.h" #include "t20012/test_case.h" #include "t20013/test_case.h" +#include "t20014/test_case.h" /// /// Package diagram tests