From 0a0b2a3d3504ad97aef85e27749e90880840b098 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Thu, 8 Dec 2022 00:41:54 +0100 Subject: [PATCH] Added template method specialization sequence diagram test case --- .../visitor/translation_unit_visitor.cc | 54 ++++++++++++------- .../visitor/translation_unit_visitor.h | 2 +- tests/t20012/t20012.cc | 20 +++++++ tests/t20012/test_case.h | 21 ++++---- tests/t20015/t20015.cc | 1 + tests/t20016/.clang-uml | 14 +++++ tests/t20016/t20016.cc | 24 +++++++++ tests/t20016/test_case.h | 46 ++++++++++++++++ tests/test_cases.cc | 1 + tests/test_cases.yaml | 3 ++ 10 files changed, 157 insertions(+), 29 deletions(-) create mode 100644 tests/t20016/.clang-uml create mode 100644 tests/t20016/t20016.cc create mode 100644 tests/t20016/test_case.h diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index ab29c275..d97096e0 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -616,23 +616,28 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr) } } else { - if (!process_function_call_expression(m, expr)) - return true; - } + if (!process_function_call_expression(m, expr)) { + // expr->dump(); + LOG_DBG("Skipping call to unsupported type of call expression " + "at: {}", + expr->getBeginLoc().printToString(source_manager())); - // - // This crashes on LLVM <= 12, for now just return empty type - // - // const auto &return_type = - // function_call_expr->getCallReturnType(current_ast_context); - // m.return_type = return_type.getAsString(); - m.return_type = ""; + return true; + } + } } + // + // This crashes on LLVM <= 12, for now just return empty type + // + // const auto &return_type = + // function_call_expr->getCallReturnType(current_ast_context); + // m.return_type = return_type.getAsString(); + m.return_type = ""; + if (m.from > 0 && m.to > 0) { if (diagram().sequences.find(m.from) == diagram().sequences.end()) { activity a; - // a.usr = m.from; a.from = m.from; diagram().sequences.insert({m.from, std::move(a)}); } @@ -778,6 +783,10 @@ bool translation_unit_visitor::process_class_template_method_call_expression( diagram().add_active_participant( get_unique_id(template_declaration->getID()).value()); } + else { + LOG_WARN("Cannot generate call due to unresolvable " + "CXXDependentScopeMemberExpr"); + } return true; } @@ -845,14 +854,23 @@ bool translation_unit_visitor::process_unresolved_lookup_call_expression( } bool translation_unit_visitor::is_callee_valid_template_specialization( - const clang::CXXDependentScopeMemberExpr *dependent_member_callee) const + const clang::CXXDependentScopeMemberExpr *dependent_member_expr) const { - return !dependent_member_callee->getBaseType().isNull() && - dependent_member_callee->getBaseType() - ->getAs() && - !dependent_member_callee->getBaseType() + const bool base_type_is_not_null = + !dependent_member_expr->getBaseType().isNull(); + + const bool base_type_is_specialization_type = + dependent_member_expr->getBaseType() + ->getAs() != nullptr; + + const bool base_type_is_not_pointer_type = + base_type_is_specialization_type && + !dependent_member_expr->getBaseType() ->getAs() ->isPointerType(); + + return (base_type_is_not_null && base_type_is_specialization_type && + base_type_is_not_pointer_type); } bool translation_unit_visitor::is_smart_pointer( @@ -898,8 +916,8 @@ translation_unit_visitor::create_class_declaration(clang::CXXRecordDecl *cls) int64_t local_id = static_cast(parent)->getID(); - // First check if the parent has been added to the diagram as regular - // class + // First check if the parent has been added to the diagram as + // regular class id_opt = get_unique_id(local_id); // If not, check if the parent template declaration is in the model diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.h b/src/sequence_diagram/visitor/translation_unit_visitor.h index 29bd9bfc..3bb75fdc 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.h +++ b/src/sequence_diagram/visitor/translation_unit_visitor.h @@ -206,7 +206,7 @@ private: bool is_smart_pointer(const clang::TemplateDecl *primary_template) const; bool is_callee_valid_template_specialization( - const clang::CXXDependentScopeMemberExpr *dependent_member_callee) + const clang::CXXDependentScopeMemberExpr *dependent_member_expr) const; bool process_operator_call_expression(model::message &m, diff --git a/tests/t20012/t20012.cc b/tests/t20012/t20012.cc index 50c5fe72..49e58a84 100644 --- a/tests/t20012/t20012.cc +++ b/tests/t20012/t20012.cc @@ -1,5 +1,7 @@ #include #include +#include +#include #include namespace clanguml { @@ -18,6 +20,8 @@ struct B { void bb() { bbb(); } void bbb() { } + + void eb() { } }; struct C { @@ -32,6 +36,14 @@ struct D { int add5(int arg) const { return arg + 5; } }; +class E { + std::optional> maybe_b; + std::shared_ptr a; + +public: + template void setup(F &&f) { f(maybe_b); } +}; + template struct R { R(F &&f) : f_{std::move(f)} @@ -79,6 +91,14 @@ void tmain() std::vector ints{0, 1, 2, 3, 4}; std::transform(ints.begin(), ints.end(), ints.begin(), [&d](auto i) { return d.add5(i); }); + + // TODO: Fix naming function call arguments which are lambdas + // E e; + // + // e.setup([](auto &&arg) mutable { + // // We cannot know here what 'arg' might be + // arg.value()->eb(); + // }); } } } \ No newline at end of file diff --git a/tests/t20012/test_case.h b/tests/t20012/test_case.h index 6440d03b..cb68ff3e 100644 --- a/tests/t20012/test_case.h +++ b/tests/t20012/test_case.h @@ -36,38 +36,39 @@ TEST_CASE("t20012", "[test-case][sequence]") // Check if all calls exist REQUIRE_THAT(puml, - HasCall(_A("tmain()"), _A("tmain()::(lambda t20012.cc:54:20)"), + HasCall(_A("tmain()"), _A("tmain()::(lambda t20012.cc:66:20)"), "operator()()")); REQUIRE_THAT( - puml, HasCall(_A("tmain()::(lambda t20012.cc:54:20)"), _A("A"), "a()")); + puml, HasCall(_A("tmain()::(lambda t20012.cc:66:20)"), _A("A"), "a()")); REQUIRE_THAT(puml, HasCall(_A("A"), _A("A"), "aa()")); REQUIRE_THAT(puml, HasCall(_A("A"), _A("A"), "aaa()")); REQUIRE_THAT( - puml, HasCall(_A("tmain()::(lambda t20012.cc:54:20)"), _A("B"), "b()")); + puml, HasCall(_A("tmain()::(lambda t20012.cc:66:20)"), _A("B"), "b()")); REQUIRE_THAT(puml, HasCall(_A("B"), _A("B"), "bb()")); REQUIRE_THAT(puml, HasCall(_A("B"), _A("B"), "bbb()")); REQUIRE_THAT( - puml, HasCall(_A("tmain()::(lambda t20012.cc:67:20)"), _A("C"), "c()")); + puml, HasCall(_A("tmain()::(lambda t20012.cc:79:20)"), _A("C"), "c()")); REQUIRE_THAT(puml, HasCall(_A("C"), _A("C"), "cc()")); REQUIRE_THAT(puml, HasCall(_A("C"), _A("C"), "ccc()")); REQUIRE_THAT(puml, - HasCall(_A("tmain()::(lambda t20012.cc:67:20)"), - _A("tmain()::(lambda t20012.cc:54:20)"), "operator()()")); + HasCall(_A("tmain()::(lambda t20012.cc:79:20)"), + _A("tmain()::(lambda t20012.cc:66:20)"), "operator()()")); REQUIRE_THAT(puml, HasCall(_A("C"), _A("C"), "ccc()")); REQUIRE_THAT(puml, - HasCall(_A("tmain()"), _A("R"), "r()")); + HasCall(_A("tmain()"), _A("R"), "r()")); REQUIRE_THAT(puml, - HasCall(_A("R"), - _A("tmain()::(lambda t20012.cc:73:9)"), "operator()()")); + HasCall(_A("R"), + _A("tmain()::(lambda t20012.cc:85:9)"), "operator()()")); REQUIRE_THAT( - puml, HasCall(_A("tmain()::(lambda t20012.cc:73:9)"), _A("C"), "c()")); + puml, HasCall(_A("tmain()::(lambda t20012.cc:85:9)"), _A("C"), "c()")); REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("D"), "add5(int)")); + save_puml( "./" + config.output_directory() + "/" + diagram->name + ".puml", puml); } \ No newline at end of file diff --git a/tests/t20015/t20015.cc b/tests/t20015/t20015.cc index 0c999632..d0d643a7 100644 --- a/tests/t20015/t20015.cc +++ b/tests/t20015/t20015.cc @@ -1,4 +1,5 @@ #include +#include namespace clanguml { namespace t20015 { diff --git a/tests/t20016/.clang-uml b/tests/t20016/.clang-uml new file mode 100644 index 00000000..1cf1f97f --- /dev/null +++ b/tests/t20016/.clang-uml @@ -0,0 +1,14 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t20016_sequence: + type: sequence + glob: + - ../../tests/t20016/t20016.cc + include: + namespaces: + - clanguml::t20016 + using_namespace: + - clanguml::t20016 + start_from: + - function: "clanguml::t20016::tmain()" \ No newline at end of file diff --git a/tests/t20016/t20016.cc b/tests/t20016/t20016.cc new file mode 100644 index 00000000..228aad6b --- /dev/null +++ b/tests/t20016/t20016.cc @@ -0,0 +1,24 @@ +namespace clanguml { +namespace t20016 { +struct A { + void a1(int a) { } + template T a2(const T &a) { return a;} +}; + +template struct B { + void b1(T b) { a_.a1(1); } + + template F b2(T t) { return a_.a2(t); } + + A a_; +}; + +void tmain() { + B b; + + b.b1(1); + + b.b2(2); +} +} +} \ No newline at end of file diff --git a/tests/t20016/test_case.h b/tests/t20016/test_case.h new file mode 100644 index 00000000..537e94c7 --- /dev/null +++ b/tests/t20016/test_case.h @@ -0,0 +1,46 @@ +/** + * tests/t20016/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("t20016", "[test-case][sequence]") +{ + auto [config, db] = load_config("t20016"); + + auto diagram = config.diagrams["t20016_sequence"]; + + REQUIRE(diagram->name == "t20016_sequence"); + + auto model = generate_sequence_diagram(*db, diagram); + + REQUIRE(model->name() == "t20016_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(long)")); + REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "a1(int)")); + + REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B"), "b2(long)")); + REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "a2(const long &)")); + + 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 fe09b0be..bff8d7be 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -262,6 +262,7 @@ using namespace clanguml::test::matchers; #include "t20013/test_case.h" #include "t20014/test_case.h" #include "t20015/test_case.h" +#include "t20016/test_case.h" /// /// Package diagram tests diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index a7c8d4db..e0b10242 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -193,6 +193,9 @@ test_cases: - name: t20015 title: Class exclusion by namespace in sequence diagram test case description: + - name: t20016 + title: Template method specialization sequence diagram test case + description: Package diagrams: - name: t30001 title: Basic package diagram test case