From a319bd0edea1856bfe4a2332738dabe6b03b70d2 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Thu, 25 Jul 2024 13:38:16 +0200 Subject: [PATCH] Enabled advanced diagram filters in sequence diagrams (#289) --- src/common/model/filters/diagram_filter.cc | 6 +++ src/common/model/filters/diagram_filter.h | 3 ++ src/common/visitor/translation_unit_visitor.h | 4 +- src/sequence_diagram/model/participant.h | 2 +- .../visitor/translation_unit_visitor.cc | 39 +++++++------------ tests/t20055/.clang-uml | 15 +++++++ tests/t20055/t20055.cc | 34 ++++++++++++++++ tests/t20055/test_case.h | 39 +++++++++++++++++++ tests/t30015/src/app.cppm | 19 --------- tests/test_cases.cc | 1 + tests/test_cases.h | 12 +++++- tests/test_cases.yaml | 3 ++ 12 files changed, 129 insertions(+), 48 deletions(-) create mode 100644 tests/t20055/.clang-uml create mode 100644 tests/t20055/t20055.cc create mode 100644 tests/t20055/test_case.h diff --git a/src/common/model/filters/diagram_filter.cc b/src/common/model/filters/diagram_filter.cc index c16033f4..d712cd47 100644 --- a/src/common/model/filters/diagram_filter.cc +++ b/src/common/model/filters/diagram_filter.cc @@ -389,6 +389,12 @@ tvl::value_t namespace_filter::match(const diagram &d, const element &e) const return result; } +tvl::value_t namespace_filter::match( + const diagram &d, const sequence_diagram::model::participant &p) const +{ + return match(d, dynamic_cast(p)); +} + modules_filter::modules_filter( filter_t type, std::vector modules) : filter_visitor{type} diff --git a/src/common/model/filters/diagram_filter.h b/src/common/model/filters/diagram_filter.h index 0b7a7792..3768bc13 100644 --- a/src/common/model/filters/diagram_filter.h +++ b/src/common/model/filters/diagram_filter.h @@ -232,6 +232,9 @@ struct namespace_filter : public filter_visitor { tvl::value_t match(const diagram &d, const element &e) const override; + tvl::value_t match(const diagram &d, + const sequence_diagram::model::participant &p) const override; + private: std::vector namespaces_; }; diff --git a/src/common/visitor/translation_unit_visitor.h b/src/common/visitor/translation_unit_visitor.h index c4569c7c..824a5c54 100644 --- a/src/common/visitor/translation_unit_visitor.h +++ b/src/common/visitor/translation_unit_visitor.h @@ -289,7 +289,7 @@ public: return stripped_comment; } - bool skip_system_header_decl(const clang::NamedDecl *decl) + bool skip_system_header_decl(const clang::NamedDecl *decl) const { return !config().include_system_headers() && source_manager().isInSystemHeader( @@ -302,7 +302,7 @@ public: * @param decl Clang declaration. * @return True, if the entity should be included in the diagram. */ - bool should_include(const clang::NamedDecl *decl) + bool should_include(const clang::NamedDecl *decl) const { if (decl == nullptr) return false; diff --git a/src/sequence_diagram/model/participant.h b/src/sequence_diagram/model/participant.h index ea5550c0..6c2b9be6 100644 --- a/src/sequence_diagram/model/participant.h +++ b/src/sequence_diagram/model/participant.h @@ -380,7 +380,7 @@ private: struct method : public function { method(const common::model::namespace_ &using_namespace); - method(const function &) = delete; + method(const method &) = delete; method(method &&) noexcept = delete; method &operator=(const method &) = delete; method &operator=(method &&) = delete; diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index 34a53068..dae80f38 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -1085,6 +1085,9 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr) auto generated_message_from_comment = generate_message_from_comment(m); if (!generated_message_from_comment && !should_include(expr)) { + LOG_DBG("Skipping call expression due to filter at: {}", + expr->getBeginLoc().printToString(source_manager())); + processed_comments().erase(raw_expr_comment); return true; } @@ -1178,7 +1181,7 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr) auto success = process_function_call_expression(m, expr); if (!success) { - LOG_DBG("Skipping call to call expression at: {}", + LOG_DBG("Skipping call expression at: {}", expr->getBeginLoc().printToString(source_manager())); return true; @@ -2098,6 +2101,7 @@ translation_unit_visitor::create_lambda_method_model( ns.pop_back(); method_model_ptr->set_name(ns.name()); ns.pop_back(); + method_model_ptr->set_namespace(ns); method_model_ptr->is_defaulted(declaration->isDefaulted()); method_model_ptr->is_assignment(declaration->isCopyAssignmentOperator() || @@ -2137,6 +2141,7 @@ translation_unit_visitor::create_method_model(clang::CXXMethodDecl *declaration) ns.pop_back(); method_model_ptr->set_name(ns.name()); ns.pop_back(); + method_model_ptr->set_namespace(ns); method_model_ptr->is_defaulted(declaration->isDefaulted()); method_model_ptr->is_assignment(declaration->isCopyAssignmentOperator() || @@ -2191,14 +2196,8 @@ translation_unit_visitor::create_method_model(clang::CXXMethodDecl *declaration) bool translation_unit_visitor::should_include(const clang::TagDecl *decl) const { - if (source_manager().isInSystemHeader(decl->getSourceRange().getBegin())) - return false; - - const auto decl_file = decl->getLocation().printToString(source_manager()); - - return diagram().should_include( - namespace_{decl->getQualifiedNameAsString()}) && - diagram().should_include(common::model::source_file{decl_file}); + return visitor_specialization_t::should_include( + dynamic_cast(decl)); } bool translation_unit_visitor::should_include( @@ -2236,8 +2235,11 @@ bool translation_unit_visitor::should_include(const clang::CallExpr *expr) const if (callee_decl != nullptr) { const auto *callee_function = callee_decl->getAsFunction(); - if ((callee_function == nullptr) || !should_include(callee_function)) + if ((callee_function == nullptr) || !should_include(callee_function)) { + LOG_DBG("Skipping call expression at {}", + expr->getBeginLoc().printToString(source_manager())); return false; + } return should_include(callee_function); } @@ -2263,30 +2265,19 @@ bool translation_unit_visitor::should_include( bool translation_unit_visitor::should_include( const clang::FunctionDecl *decl) const { - const auto decl_file = decl->getLocation().printToString(source_manager()); - - return diagram().should_include( - namespace_{decl->getQualifiedNameAsString()}) && - diagram().should_include(common::model::source_file{decl_file}); + return visitor_specialization_t::should_include(decl); } bool translation_unit_visitor::should_include( const clang::FunctionTemplateDecl *decl) const { - return should_include(decl->getAsFunction()); + return visitor_specialization_t::should_include(decl->getAsFunction()); } bool translation_unit_visitor::should_include( const clang::ClassTemplateDecl *decl) const { - if (source_manager().isInSystemHeader(decl->getSourceRange().getBegin())) - return false; - - const auto decl_file = decl->getLocation().printToString(source_manager()); - - return diagram().should_include( - namespace_{decl->getQualifiedNameAsString()}) && - diagram().should_include(common::model::source_file{decl_file}); + return visitor_specialization_t::should_include(decl); } std::optional translation_unit_visitor::get_expression_comment( diff --git a/tests/t20055/.clang-uml b/tests/t20055/.clang-uml new file mode 100644 index 00000000..a31a348d --- /dev/null +++ b/tests/t20055/.clang-uml @@ -0,0 +1,15 @@ +diagrams: + t20055_sequence: + type: sequence + filter_mode: advanced + glob: + - t20055.cc + include: + anyof: + namespaces: + - clanguml::t20055::ns2 + elements: + - clanguml::t20055::ns1::B + using_namespace: clanguml::t20055 + from: + - function: "clanguml::t20055::ns2::tmain()" \ No newline at end of file diff --git a/tests/t20055/t20055.cc b/tests/t20055/t20055.cc new file mode 100644 index 00000000..f30432bd --- /dev/null +++ b/tests/t20055/t20055.cc @@ -0,0 +1,34 @@ +namespace clanguml { +namespace t20055 { +namespace ns1 { + +struct A { + void a() { } +}; + +struct B { + A a; + void b() { a.a(); } +}; + +} // namespace ns1 +namespace ns2 { +void f() { } +struct C { + ns1::B b; + void c() + { + b.b(); + f(); + } +}; + +void tmain() +{ + C c; + c.c(); +} + +} // namespace ns2 +} +} \ No newline at end of file diff --git a/tests/t20055/test_case.h b/tests/t20055/test_case.h new file mode 100644 index 00000000..e89a53c6 --- /dev/null +++ b/tests/t20055/test_case.h @@ -0,0 +1,39 @@ +/** + * tests/t20055/test_case.h + * + * Copyright (c) 2021-2024 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("t20055") +{ + using namespace clanguml::test; + using namespace std::string_literals; + + auto [config, db, diagram, model] = + CHECK_SEQUENCE_MODEL("t20055", "t20055_sequence"); + + CHECK_SEQUENCE_DIAGRAM(*config, diagram, *model, [](const auto &src) { + REQUIRE(MessageOrder(src, + { + // + {{"ns2", "tmain()"}, {"ns2", "C"}, "c()"}, + {{"ns2", "C"}, {"ns1", "B"}, "b()"}, + {{"ns2", "C"}, {"ns2", "f()"}, ""} + // + })); + + REQUIRE(!HasMessage(src, {{"ns1", "B"}, {"ns1", "A"}, "a()"})); + }); +} \ No newline at end of file diff --git a/tests/t30015/src/app.cppm b/tests/t30015/src/app.cppm index 22da8d4e..58d188d9 100644 --- a/tests/t30015/src/app.cppm +++ b/tests/t30015/src/app.cppm @@ -9,25 +9,6 @@ module; export module t30015.app; import t30015.lib1; -// import t30015.app; -// import t30015.mod2; -// import t30015.mod3; -// import t30015.mod4; -// import t30015.mod5; -// import t30015.mod6; -// import t30015.mod7; -// import t30015.mod8; -// import t30015.mod9; -// import t30015.mod10; -// import t30015.mod11; -// import t30015.mod12; -// import t30015.mod13; -// import t30015.mod14; -// import t30015.mod15; -// import t30015.mod16; -// import t30015.mod17; -// import t30015.mod18; - export namespace clanguml::t30015 { class CBA : public CF { diff --git a/tests/test_cases.cc b/tests/test_cases.cc index 0121de51..1994881c 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -623,6 +623,7 @@ void CHECK_INCLUDE_DIAGRAM(const clanguml::config::config &config, #include "t20052/test_case.h" #include "t20053/test_case.h" #include "t20054/test_case.h" +#include "t20055/test_case.h" /// /// Package diagram tests diff --git a/tests/test_cases.h b/tests/test_cases.h index 1716817f..7b923ec3 100644 --- a/tests/test_cases.h +++ b/tests/test_cases.h @@ -339,8 +339,16 @@ std::optional get_participant( if (!j.contains("participants")) return {}; + std::string using_namespace{}; + if (j.contains("using_namespace")) { + using_namespace = + fmt::format("{}::", j["using_namespace"].get()); + } + for (const nlohmann::json &e : j.at("participants")) { - if (e["display_name"] == name) + if (e["display_name"] == name || + e["full_name"].get().substr(using_namespace.size()) == + name) return {e}; } @@ -2482,7 +2490,7 @@ int64_t FindMessage( if (!fail) return -1; - std::cout << "FindMessage failed with error " << e.what() << "\n"; + std::cout << "FindMessage failed with error: " << e.what() << "\n"; throw e; } diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index 98081ac8..e623eb34 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -409,6 +409,9 @@ test_cases: - name: t20054 title: Test case for sequence diagram with nested classes description: + - name: t20055 + title: Test case for advanced filter in sequence diagram + description: Package diagrams: - name: t30001 title: Basic package diagram test case