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;
}
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(
filter_t type, std::vector<common::string_or_regex> modules)
: 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 sequence_diagram::model::participant &p) const override;
private:
std::vector<common::namespace_or_regex> namespaces_;
};

View File

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

View File

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

View File

@@ -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<const clang::NamedDecl *>(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<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;
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 {

View File

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

View File

@@ -339,8 +339,16 @@ std::optional<nlohmann::json> get_participant(
if (!j.contains("participants"))
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")) {
if (e["display_name"] == name)
if (e["display_name"] == name ||
e["full_name"].get<std::string>().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;
}

View File

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