Enabled advanced diagram filters in sequence diagrams (#289)

This commit is contained in:
Bartek Kryza
2024-07-25 13:38:16 +02:00
parent d5687907e0
commit a319bd0ede
12 changed files with 129 additions and 48 deletions

View File

@@ -389,6 +389,12 @@ tvl::value_t namespace_filter::match(const diagram &d, const element &e) const
return result; return result;
} }
tvl::value_t namespace_filter::match(
const diagram &d, const sequence_diagram::model::participant &p) const
{
return match(d, dynamic_cast<const element &>(p));
}
modules_filter::modules_filter( modules_filter::modules_filter(
filter_t type, std::vector<common::string_or_regex> modules) filter_t type, std::vector<common::string_or_regex> modules)
: filter_visitor{type} : filter_visitor{type}

View File

@@ -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 element &e) const override;
tvl::value_t match(const diagram &d,
const sequence_diagram::model::participant &p) const override;
private: private:
std::vector<common::namespace_or_regex> namespaces_; std::vector<common::namespace_or_regex> namespaces_;
}; };

View File

@@ -289,7 +289,7 @@ public:
return stripped_comment; 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() && return !config().include_system_headers() &&
source_manager().isInSystemHeader( source_manager().isInSystemHeader(
@@ -302,7 +302,7 @@ public:
* @param decl Clang declaration. * @param decl Clang declaration.
* @return True, if the entity should be included in the diagram. * @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) if (decl == nullptr)
return false; return false;

View File

@@ -380,7 +380,7 @@ private:
struct method : public function { struct method : public function {
method(const common::model::namespace_ &using_namespace); method(const common::model::namespace_ &using_namespace);
method(const function &) = delete; method(const method &) = delete;
method(method &&) noexcept = delete; method(method &&) noexcept = delete;
method &operator=(const method &) = delete; method &operator=(const method &) = delete;
method &operator=(method &&) = delete; method &operator=(method &&) = delete;

View File

@@ -1085,6 +1085,9 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
auto generated_message_from_comment = generate_message_from_comment(m); auto generated_message_from_comment = generate_message_from_comment(m);
if (!generated_message_from_comment && !should_include(expr)) { 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); processed_comments().erase(raw_expr_comment);
return true; return true;
} }
@@ -1178,7 +1181,7 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
auto success = process_function_call_expression(m, expr); auto success = process_function_call_expression(m, expr);
if (!success) { if (!success) {
LOG_DBG("Skipping call to call expression at: {}", LOG_DBG("Skipping call expression at: {}",
expr->getBeginLoc().printToString(source_manager())); expr->getBeginLoc().printToString(source_manager()));
return true; return true;
@@ -2098,6 +2101,7 @@ translation_unit_visitor::create_lambda_method_model(
ns.pop_back(); ns.pop_back();
method_model_ptr->set_name(ns.name()); method_model_ptr->set_name(ns.name());
ns.pop_back(); ns.pop_back();
method_model_ptr->set_namespace(ns);
method_model_ptr->is_defaulted(declaration->isDefaulted()); method_model_ptr->is_defaulted(declaration->isDefaulted());
method_model_ptr->is_assignment(declaration->isCopyAssignmentOperator() || method_model_ptr->is_assignment(declaration->isCopyAssignmentOperator() ||
@@ -2137,6 +2141,7 @@ translation_unit_visitor::create_method_model(clang::CXXMethodDecl *declaration)
ns.pop_back(); ns.pop_back();
method_model_ptr->set_name(ns.name()); method_model_ptr->set_name(ns.name());
ns.pop_back(); ns.pop_back();
method_model_ptr->set_namespace(ns);
method_model_ptr->is_defaulted(declaration->isDefaulted()); method_model_ptr->is_defaulted(declaration->isDefaulted());
method_model_ptr->is_assignment(declaration->isCopyAssignmentOperator() || 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 bool translation_unit_visitor::should_include(const clang::TagDecl *decl) const
{ {
if (source_manager().isInSystemHeader(decl->getSourceRange().getBegin())) return visitor_specialization_t::should_include(
return false; dynamic_cast<const clang::NamedDecl *>(decl));
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});
} }
bool translation_unit_visitor::should_include( 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) { if (callee_decl != nullptr) {
const auto *callee_function = callee_decl->getAsFunction(); 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 false;
}
return should_include(callee_function); return should_include(callee_function);
} }
@@ -2263,30 +2265,19 @@ bool translation_unit_visitor::should_include(
bool translation_unit_visitor::should_include( bool translation_unit_visitor::should_include(
const clang::FunctionDecl *decl) const const clang::FunctionDecl *decl) const
{ {
const auto decl_file = decl->getLocation().printToString(source_manager()); return visitor_specialization_t::should_include(decl);
return diagram().should_include(
namespace_{decl->getQualifiedNameAsString()}) &&
diagram().should_include(common::model::source_file{decl_file});
} }
bool translation_unit_visitor::should_include( bool translation_unit_visitor::should_include(
const clang::FunctionTemplateDecl *decl) const const clang::FunctionTemplateDecl *decl) const
{ {
return should_include(decl->getAsFunction()); return visitor_specialization_t::should_include(decl->getAsFunction());
} }
bool translation_unit_visitor::should_include( bool translation_unit_visitor::should_include(
const clang::ClassTemplateDecl *decl) const const clang::ClassTemplateDecl *decl) const
{ {
if (source_manager().isInSystemHeader(decl->getSourceRange().getBegin())) return visitor_specialization_t::should_include(decl);
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});
} }
std::optional<std::string> translation_unit_visitor::get_expression_comment( std::optional<std::string> translation_unit_visitor::get_expression_comment(

15
tests/t20055/.clang-uml Normal file
View File

@@ -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()"

34
tests/t20055/t20055.cc Normal file
View File

@@ -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
}
}

39
tests/t20055/test_case.h Normal file
View File

@@ -0,0 +1,39 @@
/**
* tests/t20055/test_case.h
*
* Copyright (c) 2021-2024 Bartek Kryza <bkryza@gmail.com>
*
* 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()"}));
});
}

View File

@@ -9,25 +9,6 @@ module;
export module t30015.app; export module t30015.app;
import t30015.lib1; 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 { export namespace clanguml::t30015 {
class CBA : public CF { class CBA : public CF {

View File

@@ -623,6 +623,7 @@ void CHECK_INCLUDE_DIAGRAM(const clanguml::config::config &config,
#include "t20052/test_case.h" #include "t20052/test_case.h"
#include "t20053/test_case.h" #include "t20053/test_case.h"
#include "t20054/test_case.h" #include "t20054/test_case.h"
#include "t20055/test_case.h"
/// ///
/// Package diagram tests /// Package diagram tests

View File

@@ -339,8 +339,16 @@ std::optional<nlohmann::json> get_participant(
if (!j.contains("participants")) if (!j.contains("participants"))
return {}; return {};
std::string using_namespace{};
if (j.contains("using_namespace")) {
using_namespace =
fmt::format("{}::", j["using_namespace"].get<std::string>());
}
for (const nlohmann::json &e : j.at("participants")) { for (const nlohmann::json &e : j.at("participants")) {
if (e["display_name"] == name) if (e["display_name"] == name ||
e["full_name"].get<std::string>().substr(using_namespace.size()) ==
name)
return {e}; return {e};
} }
@@ -2482,7 +2490,7 @@ int64_t FindMessage(
if (!fail) if (!fail)
return -1; return -1;
std::cout << "FindMessage failed with error " << e.what() << "\n"; std::cout << "FindMessage failed with error: " << e.what() << "\n";
throw e; throw e;
} }

View File

@@ -409,6 +409,9 @@ test_cases:
- name: t20054 - name: t20054
title: Test case for sequence diagram with nested classes title: Test case for sequence diagram with nested classes
description: description:
- name: t20055
title: Test case for advanced filter in sequence diagram
description:
Package diagrams: Package diagrams:
- name: t30001 - name: t30001
title: Basic package diagram test case title: Basic package diagram test case