Added option inline_lambda_messages to omit lambda expressions from sequence diagrams (#261)
This commit is contained in:
@@ -252,6 +252,17 @@ results in the following diagram:
|
||||
|
||||

|
||||
|
||||
In case lambda expressions are redundant and we are only interested in the calls
|
||||
generate from the lambda expressions, it is possible to inline lambda
|
||||
expressions in the generated diagrams by specifying the following option:
|
||||
|
||||
```yaml
|
||||
inline_lambda_messages: true
|
||||
```
|
||||
|
||||
For example compare the test cases [t20012](test_cases/t20012.md) and
|
||||
[t20052](test_cases/t20052.md).
|
||||
|
||||
## Customizing participants order
|
||||
The default participant order in the sequence diagram can be suboptimal in the
|
||||
sense that consecutive calls can go right, then left, then right again
|
||||
|
||||
@@ -561,6 +561,9 @@ cli_flow_t cli_handler::add_config_diagram(
|
||||
doc["diagrams"][name]["glob"] = std::vector<std::string>{{"src/*.cpp"}};
|
||||
doc["diagrams"][name]["combine_free_functions_into_file_participants"] =
|
||||
true;
|
||||
doc["diagrams"][name]["inline_lambda_messages"] = false;
|
||||
doc["diagrams"][name]["generate_message_comments"] = false;
|
||||
doc["diagrams"][name]["generate_condition_statements"] = false;
|
||||
doc["diagrams"][name]["using_namespace"] =
|
||||
std::vector<std::string>{{"myproject"}};
|
||||
doc["diagrams"][name]["include"]["paths"] =
|
||||
|
||||
@@ -40,6 +40,11 @@ class diagram {
|
||||
public:
|
||||
diagram();
|
||||
|
||||
diagram(const diagram &) = delete;
|
||||
diagram(diagram && /*unused*/) noexcept;
|
||||
diagram &operator=(const diagram &) = delete;
|
||||
diagram &operator=(diagram && /*unused*/) noexcept;
|
||||
|
||||
virtual ~diagram();
|
||||
|
||||
/**
|
||||
@@ -77,11 +82,6 @@ public:
|
||||
virtual common::optional_ref<clanguml::common::model::diagram_element>
|
||||
get_with_namespace(const std::string &name, const namespace_ &ns) const;
|
||||
|
||||
diagram(const diagram &) = delete;
|
||||
diagram(diagram && /*unused*/) noexcept;
|
||||
diagram &operator=(const diagram &) = delete;
|
||||
diagram &operator=(diagram && /*unused*/) noexcept;
|
||||
|
||||
/**
|
||||
* Set diagram name.
|
||||
*
|
||||
|
||||
@@ -236,6 +236,7 @@ void inheritable_diagram_options::inherit(
|
||||
comment_parser.override(parent.comment_parser);
|
||||
combine_free_functions_into_file_participants.override(
|
||||
parent.combine_free_functions_into_file_participants);
|
||||
inline_lambda_messages.override(parent.inline_lambda_messages);
|
||||
generate_return_types.override(parent.generate_return_types);
|
||||
generate_condition_statements.override(
|
||||
parent.generate_condition_statements);
|
||||
|
||||
@@ -570,6 +570,7 @@ struct inheritable_diagram_options {
|
||||
"comment_parser", comment_parser_t::plain};
|
||||
option<bool> combine_free_functions_into_file_participants{
|
||||
"combine_free_functions_into_file_participants", false};
|
||||
option<bool> inline_lambda_messages{"inline_lambda_messages", false};
|
||||
option<bool> generate_return_types{"generate_return_types", false};
|
||||
option<bool> generate_condition_statements{
|
||||
"generate_condition_statements", false};
|
||||
|
||||
@@ -218,6 +218,7 @@ types:
|
||||
#
|
||||
generate_method_arguments: !optional generate_method_arguments_t
|
||||
combine_free_functions_into_file_participants: !optional bool
|
||||
inline_lambda_messages: !optional bool
|
||||
generate_return_types: !optional bool
|
||||
generate_condition_statements: !optional bool
|
||||
generate_message_comments: !optional bool
|
||||
@@ -340,6 +341,7 @@ root:
|
||||
include_relations_also_as_members: !optional bool
|
||||
generate_method_arguments: !optional generate_method_arguments_t
|
||||
combine_free_functions_into_file_participants: !optional bool
|
||||
inline_lambda_messages: !optional bool
|
||||
generate_concept_requirements: !optional bool
|
||||
generate_return_types: !optional bool
|
||||
generate_condition_statements: !optional bool
|
||||
|
||||
@@ -665,6 +665,7 @@ template <> struct convert<sequence_diagram> {
|
||||
get_option(node, rhs.from_to);
|
||||
get_option(node, rhs.to);
|
||||
get_option(node, rhs.combine_free_functions_into_file_participants);
|
||||
get_option(node, rhs.inline_lambda_messages);
|
||||
get_option(node, rhs.generate_return_types);
|
||||
get_option(node, rhs.generate_condition_statements);
|
||||
get_option(node, rhs.participants_order);
|
||||
@@ -844,6 +845,7 @@ template <> struct convert<config> {
|
||||
get_option(node, rhs.debug_mode);
|
||||
get_option(node, rhs.generate_metadata);
|
||||
get_option(node, rhs.combine_free_functions_into_file_participants);
|
||||
get_option(node, rhs.inline_lambda_messages);
|
||||
get_option(node, rhs.generate_return_types);
|
||||
get_option(node, rhs.generate_condition_statements);
|
||||
get_option(node, rhs.generate_message_comments);
|
||||
|
||||
@@ -347,6 +347,7 @@ YAML::Emitter &operator<<(
|
||||
sd != nullptr) {
|
||||
out << sd->title;
|
||||
out << c.combine_free_functions_into_file_participants;
|
||||
out << c.inline_lambda_messages;
|
||||
out << c.generate_condition_statements;
|
||||
out << c.generate_method_arguments;
|
||||
out << c.generate_return_types;
|
||||
|
||||
@@ -125,8 +125,8 @@ void generator::generate_call(const message &m, std::ostream &ostr) const
|
||||
void generator::generate_return(const message &m, std::ostream &ostr) const
|
||||
{
|
||||
|
||||
// Add return activity only for messages between different actors and
|
||||
// only if the return type is different than void
|
||||
// Add return activity only for messages between different actors
|
||||
// and only if the return type is different than void
|
||||
if (m.from() == m.to())
|
||||
return;
|
||||
|
||||
@@ -338,7 +338,8 @@ void generator::generate_participant(
|
||||
auto p = model().get(name);
|
||||
|
||||
if (!p.has_value()) {
|
||||
LOG_WARN("Cannot find participant {} from `participants_order` option",
|
||||
LOG_WARN("Cannot find participant {} from `participants_order` "
|
||||
"option",
|
||||
name);
|
||||
return;
|
||||
}
|
||||
@@ -629,10 +630,10 @@ void generator::generate_diagram(std::ostream &ostr) const
|
||||
model::function::message_render_mode render_mode =
|
||||
select_method_arguments_render_mode();
|
||||
|
||||
// For methods or functions in diagrams where they are combined into
|
||||
// file participants, we need to add an 'entry' point call to know
|
||||
// which method relates to the first activity for this 'start_from'
|
||||
// condition
|
||||
// For methods or functions in diagrams where they are
|
||||
// combined into file participants, we need to add an
|
||||
// 'entry' point call to know which method relates to the
|
||||
// first activity for this 'start_from' condition
|
||||
if (from.value().type_name() == "method" ||
|
||||
config().combine_free_functions_into_file_participants()) {
|
||||
ostr << "[->"
|
||||
|
||||
@@ -95,22 +95,22 @@ void diagram::add_active_participant(common::id_t id)
|
||||
|
||||
const activity &diagram::get_activity(common::id_t id) const
|
||||
{
|
||||
return sequences_.at(id);
|
||||
return activities_.at(id);
|
||||
}
|
||||
|
||||
bool diagram::has_activity(common::id_t id) const
|
||||
{
|
||||
return sequences_.count(id) > 0;
|
||||
return activities_.count(id) > 0;
|
||||
}
|
||||
|
||||
activity &diagram::get_activity(common::id_t id) { return sequences_.at(id); }
|
||||
activity &diagram::get_activity(common::id_t id) { return activities_.at(id); }
|
||||
|
||||
void diagram::add_message(model::message &&message)
|
||||
{
|
||||
const auto caller_id = message.from();
|
||||
if (sequences_.find(caller_id) == sequences_.end()) {
|
||||
if (activities_.find(caller_id) == activities_.end()) {
|
||||
activity a{caller_id};
|
||||
sequences_.insert({caller_id, std::move(a)});
|
||||
activities_.insert({caller_id, std::move(a)});
|
||||
}
|
||||
|
||||
get_activity(caller_id).add_message(std::move(message));
|
||||
@@ -126,7 +126,7 @@ void diagram::end_block_message(
|
||||
{
|
||||
const auto caller_id = message.from();
|
||||
|
||||
if (sequences_.find(caller_id) != sequences_.end()) {
|
||||
if (activities_.find(caller_id) != activities_.end()) {
|
||||
auto ¤t_messages = get_activity(caller_id).messages();
|
||||
|
||||
fold_or_end_block_statement(
|
||||
@@ -139,7 +139,7 @@ void diagram::add_case_stmt_message(model::message &&m)
|
||||
using clanguml::common::model::message_t;
|
||||
const auto caller_id = m.from();
|
||||
|
||||
if (sequences_.find(caller_id) != sequences_.end()) {
|
||||
if (activities_.find(caller_id) != activities_.end()) {
|
||||
auto ¤t_messages = get_activity(caller_id).messages();
|
||||
|
||||
if (current_messages.back().type() == message_t::kCase) {
|
||||
@@ -151,11 +151,11 @@ void diagram::add_case_stmt_message(model::message &&m)
|
||||
}
|
||||
}
|
||||
|
||||
std::map<common::id_t, activity> &diagram::sequences() { return sequences_; }
|
||||
std::map<common::id_t, activity> &diagram::sequences() { return activities_; }
|
||||
|
||||
const std::map<common::id_t, activity> &diagram::sequences() const
|
||||
{
|
||||
return sequences_;
|
||||
return activities_;
|
||||
}
|
||||
|
||||
std::map<common::id_t, std::unique_ptr<participant>> &diagram::participants()
|
||||
@@ -191,7 +191,7 @@ std::vector<std::string> diagram::list_from_values() const
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
|
||||
for (const auto &[from_id, act] : sequences_) {
|
||||
for (const auto &[from_id, act] : activities_) {
|
||||
|
||||
const auto &from_activity = *(participants_.at(from_id));
|
||||
const auto &full_name = from_activity.full_name(false);
|
||||
@@ -209,7 +209,7 @@ std::vector<std::string> diagram::list_to_values() const
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
|
||||
for (const auto &[from_id, act] : sequences_) {
|
||||
for (const auto &[from_id, act] : activities_) {
|
||||
for (const auto &m : act.messages()) {
|
||||
if (participants_.count(m.to()) > 0) {
|
||||
const auto &to_activity = *(participants_.at(m.to()));
|
||||
@@ -406,7 +406,7 @@ std::vector<message_chain_t> diagram::get_all_from_to_message_chains(
|
||||
|
||||
bool diagram::is_empty() const
|
||||
{
|
||||
return sequences_.empty() || participants_.empty();
|
||||
return activities_.empty() || participants_.empty();
|
||||
}
|
||||
|
||||
void diagram::print() const
|
||||
@@ -417,7 +417,7 @@ void diagram::print() const
|
||||
}
|
||||
|
||||
LOG_TRACE(" --- Activities ---");
|
||||
for (const auto &[from_id, act] : sequences_) {
|
||||
for (const auto &[from_id, act] : activities_) {
|
||||
if (participants_.count(from_id) == 0)
|
||||
continue;
|
||||
|
||||
@@ -487,7 +487,7 @@ void diagram::finalize()
|
||||
|
||||
// First in each sequence (activity) filter out any remaining
|
||||
// uninteresting calls
|
||||
for (auto &[id, act] : sequences_) {
|
||||
for (auto &[id, act] : activities_) {
|
||||
util::erase_if(act.messages(), [this](auto &m) {
|
||||
if (m.type() != message_t::kCall)
|
||||
return false;
|
||||
@@ -507,7 +507,7 @@ void diagram::finalize()
|
||||
}
|
||||
|
||||
// Now remove any empty block statements, e.g. if/endif
|
||||
for (auto &[id, act] : sequences_) {
|
||||
for (auto &[id, act] : activities_) {
|
||||
int64_t block_nest_level{0};
|
||||
std::vector<std::vector<message>> block_message_stack;
|
||||
// Add first stack level - this level will contain the filtered
|
||||
|
||||
@@ -295,6 +295,13 @@ public:
|
||||
*/
|
||||
bool is_empty() const override;
|
||||
|
||||
void update_sequences_from_diagram(diagram &other)
|
||||
{
|
||||
activities_ = std::move(other.activities_);
|
||||
participants_ = std::move(other.participants_);
|
||||
active_participants_ = std::move(other.active_participants_);
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* This method checks the last messages in sequence (current_messages),
|
||||
@@ -337,7 +344,7 @@ private:
|
||||
return block_end_types.count(mt) > 0;
|
||||
};
|
||||
|
||||
std::map<common::id_t, activity> sequences_;
|
||||
std::map<common::id_t, activity> activities_;
|
||||
|
||||
std::map<common::id_t, std::unique_ptr<participant>> participants_;
|
||||
|
||||
|
||||
@@ -630,7 +630,7 @@ bool translation_unit_visitor::TraverseCXXConstructExpr(
|
||||
|
||||
translation_unit_visitor::VisitCXXConstructExpr(expr);
|
||||
|
||||
LOG_TRACE("Leaving member call expression at {}",
|
||||
LOG_TRACE("Leaving cxx construct call expression at {}",
|
||||
expr->getBeginLoc().printToString(source_manager()));
|
||||
|
||||
context().leave_callexpr();
|
||||
@@ -2018,6 +2018,38 @@ void translation_unit_visitor::pop_message_to_diagram(
|
||||
}
|
||||
|
||||
void translation_unit_visitor::finalize()
|
||||
{
|
||||
resolve_ids_to_global();
|
||||
|
||||
// Change all messages with target set to an id of a lambda expression to
|
||||
// to the ID of their operator() - this is necessary, as some calls to
|
||||
// lambda expressions are visited before the actual lambda expressions
|
||||
// are visited...
|
||||
ensure_lambda_messages_have_operator_as_target();
|
||||
|
||||
if (config().inline_lambda_messages())
|
||||
inline_lambda_operator_calls();
|
||||
}
|
||||
|
||||
void translation_unit_visitor::ensure_lambda_messages_have_operator_as_target()
|
||||
{
|
||||
for (auto &[id, activity] : diagram().sequences()) {
|
||||
for (auto &m : activity.messages()) {
|
||||
auto participant = diagram().get_participant<model::class_>(m.to());
|
||||
|
||||
if (participant && participant.value().is_lambda() &&
|
||||
participant.value().lambda_operator_id() != 0) {
|
||||
LOG_DBG("Changing lambda expression target id from {} to {}",
|
||||
m.to(), participant.value().lambda_operator_id());
|
||||
|
||||
m.set_to(participant.value().lambda_operator_id());
|
||||
m.set_message_name("operator()");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void translation_unit_visitor::resolve_ids_to_global()
|
||||
{
|
||||
std::set<common::id_t> active_participants_unique;
|
||||
|
||||
@@ -2041,25 +2073,116 @@ void translation_unit_visitor::finalize()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Change all messages with target set to an id of a lambda expression to
|
||||
// to the ID of their operator() - this is necessary, as some calls to
|
||||
// lambda expressions are visited before the actual lambda expressions
|
||||
// are visited...
|
||||
for (auto &[id, activity] : diagram().sequences()) {
|
||||
for (auto &m : activity.messages()) {
|
||||
auto participant = diagram().get_participant<model::class_>(m.to());
|
||||
void translation_unit_visitor::inline_lambda_operator_calls()
|
||||
{ // If option to inline lambda calls is enabled, we need to modify the
|
||||
// sequences to skip the lambda calls. In case lambda call does not lead
|
||||
// to a non-lambda call, omit it entirely
|
||||
|
||||
if (participant && participant.value().is_lambda() &&
|
||||
participant.value().lambda_operator_id() != 0) {
|
||||
LOG_DBG("Changing lambda expression target id from {} to {}",
|
||||
m.to(), participant.value().lambda_operator_id());
|
||||
model::diagram lambdaless_diagram;
|
||||
|
||||
m.set_to(participant.value().lambda_operator_id());
|
||||
m.set_message_name("operator()");
|
||||
for (auto &[id, act] : diagram().sequences()) {
|
||||
model::activity new_activity{id};
|
||||
|
||||
// If activity is a lambda operator() - skip it
|
||||
auto maybe_lambda_activity =
|
||||
diagram().get_participant<model::method>(id);
|
||||
|
||||
if (maybe_lambda_activity) {
|
||||
const auto parent_class_id =
|
||||
maybe_lambda_activity.value().class_id();
|
||||
auto maybe_parent_class =
|
||||
diagram().get_participant<model::class_>(parent_class_id);
|
||||
|
||||
if (maybe_parent_class && maybe_parent_class.value().is_lambda()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// For other activities, check each message - if it calls lambda
|
||||
// operator() - reattach the message to the next activity in the chain
|
||||
// (assuming it's not lambda)
|
||||
for (auto &m : act.messages()) {
|
||||
|
||||
auto message_call_to_lambda{false};
|
||||
|
||||
message_call_to_lambda =
|
||||
inline_lambda_operator_call(id, new_activity, m);
|
||||
|
||||
if (!message_call_to_lambda)
|
||||
new_activity.add_message(m);
|
||||
}
|
||||
|
||||
// Add activity
|
||||
lambdaless_diagram.sequences().insert({id, std::move(new_activity)});
|
||||
}
|
||||
|
||||
for (auto &&[id, p] : diagram().participants()) {
|
||||
// Skip participants which are lambda classes
|
||||
if (const auto *maybe_class =
|
||||
dynamic_cast<const model::class_ *>(p.get());
|
||||
maybe_class && maybe_class->is_lambda()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip participants which are lambda operator methods
|
||||
if (const auto *maybe_method =
|
||||
dynamic_cast<const model::method *>(p.get());
|
||||
maybe_method) {
|
||||
auto maybe_class = diagram().get_participant<model::class_>(
|
||||
maybe_method->class_id());
|
||||
if (maybe_class && maybe_class.value().is_lambda())
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise move the participant to the new diagram model
|
||||
lambdaless_diagram.add_participant(std::move(p));
|
||||
}
|
||||
|
||||
// Skip active participants which are not in lambdaless_diagram participants
|
||||
for (auto id : diagram().active_participants()) {
|
||||
if (lambdaless_diagram.participants().count(id)) {
|
||||
lambdaless_diagram.add_active_participant(id);
|
||||
}
|
||||
}
|
||||
|
||||
diagram().update_sequences_from_diagram(lambdaless_diagram);
|
||||
}
|
||||
|
||||
bool translation_unit_visitor::inline_lambda_operator_call(
|
||||
const long id, model::activity &new_activity, const model::message &m)
|
||||
{
|
||||
bool message_call_to_lambda{false};
|
||||
auto maybe_lambda_operator =
|
||||
diagram().get_participant<model::method>(m.to());
|
||||
|
||||
if (maybe_lambda_operator) {
|
||||
const auto parent_class_id = maybe_lambda_operator.value().class_id();
|
||||
auto maybe_parent_class =
|
||||
diagram().get_participant<model::class_>(parent_class_id);
|
||||
|
||||
if (maybe_parent_class && maybe_parent_class.value().is_lambda()) {
|
||||
// auto new_message{m};
|
||||
// new_message.set_
|
||||
auto lambda_operator_activity = diagram().get_activity(m.to());
|
||||
|
||||
// For each call in that lambda activity - reattach this
|
||||
// call to the current activity
|
||||
for (auto &mm : lambda_operator_activity.messages()) {
|
||||
if (!inline_lambda_operator_call(id, new_activity, mm)) {
|
||||
auto new_message{mm};
|
||||
|
||||
new_message.set_from(id);
|
||||
new_activity.add_message(new_message);
|
||||
}
|
||||
}
|
||||
|
||||
message_call_to_lambda = true;
|
||||
}
|
||||
}
|
||||
|
||||
return message_call_to_lambda;
|
||||
}
|
||||
|
||||
std::unique_ptr<clanguml::sequence_diagram::model::method>
|
||||
|
||||
@@ -487,6 +487,11 @@ private:
|
||||
*/
|
||||
template_builder_t &tbuilder() { return template_builder_; }
|
||||
|
||||
void inline_lambda_operator_calls();
|
||||
|
||||
bool inline_lambda_operator_call(
|
||||
const long id, model::activity &new_activity, const model::message &m);
|
||||
|
||||
call_expression_context call_expression_context_;
|
||||
|
||||
/**
|
||||
@@ -522,5 +527,7 @@ private:
|
||||
processed_comments_by_caller_id_;
|
||||
|
||||
template_builder_t template_builder_;
|
||||
void resolve_ids_to_global();
|
||||
void ensure_lambda_messages_have_operator_as_target();
|
||||
};
|
||||
} // namespace clanguml::sequence_diagram::visitor
|
||||
|
||||
12
tests/t20052/.clang-uml
Normal file
12
tests/t20052/.clang-uml
Normal file
@@ -0,0 +1,12 @@
|
||||
diagrams:
|
||||
t20052_sequence:
|
||||
type: sequence
|
||||
glob:
|
||||
- t20052.cc
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::t20052
|
||||
using_namespace: clanguml::t20052
|
||||
inline_lambda_messages: true
|
||||
from:
|
||||
- function: "clanguml::t20052::tmain()"
|
||||
105
tests/t20052/t20052.cc
Normal file
105
tests/t20052/t20052.cc
Normal file
@@ -0,0 +1,105 @@
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace clanguml {
|
||||
namespace t20052 {
|
||||
struct A {
|
||||
void a() { aa(); }
|
||||
|
||||
void aa() { aaa(); }
|
||||
|
||||
void aaa() { }
|
||||
};
|
||||
|
||||
struct B {
|
||||
void b() { bb(); }
|
||||
|
||||
void bb() { bbb(); }
|
||||
|
||||
void bbb() { }
|
||||
|
||||
void eb() { }
|
||||
};
|
||||
|
||||
struct C {
|
||||
void c() { cc(); }
|
||||
|
||||
void cc() { ccc(); }
|
||||
|
||||
void ccc() { }
|
||||
};
|
||||
|
||||
struct D {
|
||||
int add5(int arg) const { return arg + 5; }
|
||||
};
|
||||
|
||||
class E {
|
||||
std::optional<std::shared_ptr<B>> maybe_b;
|
||||
std::shared_ptr<A> a;
|
||||
|
||||
public:
|
||||
template <typename F> void setup(F &&f) { f(maybe_b); }
|
||||
};
|
||||
|
||||
template <typename F> struct R {
|
||||
R(F &&f)
|
||||
: f_{std::move(f)}
|
||||
{
|
||||
}
|
||||
|
||||
void r() { f_(); }
|
||||
|
||||
F f_;
|
||||
};
|
||||
|
||||
void tmain()
|
||||
{
|
||||
A a;
|
||||
B b;
|
||||
C c;
|
||||
|
||||
// The activity shouldn't be marked at the lambda definition, but
|
||||
// wherever it is actually called...
|
||||
auto alambda = [&a, &b]() {
|
||||
a.a();
|
||||
b.b();
|
||||
};
|
||||
|
||||
// ...like here
|
||||
alambda();
|
||||
|
||||
// There should be no call to B in the sequence diagram as the blambda
|
||||
// is never called
|
||||
[[maybe_unused]] auto blambda = [&b]() { b.b(); };
|
||||
|
||||
// Nested lambdas should also work
|
||||
auto clambda = [alambda, &c]() {
|
||||
c.c();
|
||||
alambda();
|
||||
};
|
||||
clambda();
|
||||
|
||||
R r{[&c]() { c.c(); }};
|
||||
|
||||
r.r();
|
||||
|
||||
D d;
|
||||
|
||||
std::vector<int> 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();
|
||||
// });
|
||||
}
|
||||
}
|
||||
}
|
||||
176
tests/t20052/test_case.h
Normal file
176
tests/t20052/test_case.h
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* tests/t20052/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("t20052", "[test-case][sequence]")
|
||||
{
|
||||
auto [config, db] = load_config("t20052");
|
||||
|
||||
auto diagram = config.diagrams["t20052_sequence"];
|
||||
|
||||
REQUIRE(diagram->name == "t20052_sequence");
|
||||
|
||||
auto model = generate_sequence_diagram(*db, diagram);
|
||||
|
||||
REQUIRE(model->name() == "t20052_sequence");
|
||||
|
||||
{
|
||||
auto src = generate_sequence_puml(diagram, *model);
|
||||
AliasMatcher _A(src);
|
||||
|
||||
REQUIRE_THAT(src, StartsWith("@startuml"));
|
||||
REQUIRE_THAT(src, EndsWith("@enduml\n"));
|
||||
|
||||
// Check if all calls exist
|
||||
REQUIRE_THAT(src,
|
||||
!HasCall(_A("tmain()"), _A("tmain()::(lambda t20052.cc:67:20)"),
|
||||
"operator()() const"));
|
||||
REQUIRE_THAT(src,
|
||||
!HasCall(_A("tmain()::(lambda t20052.cc:67:20)"), _A("A"), "a()"));
|
||||
|
||||
REQUIRE_THAT(src, HasCall(_A("tmain()"), _A("A"), "a()"));
|
||||
REQUIRE_THAT(src, HasCall(_A("A"), _A("A"), "aa()"));
|
||||
REQUIRE_THAT(src, HasCall(_A("A"), _A("A"), "aaa()"));
|
||||
|
||||
REQUIRE_THAT(src,
|
||||
!HasCall(_A("tmain()::(lambda t20052.cc:67:20)"), _A("B"), "b()"));
|
||||
|
||||
REQUIRE_THAT(src, HasCall(_A("tmain()"), _A("B"), "b()"));
|
||||
REQUIRE_THAT(src, HasCall(_A("B"), _A("B"), "bb()"));
|
||||
REQUIRE_THAT(src, HasCall(_A("B"), _A("B"), "bbb()"));
|
||||
|
||||
REQUIRE_THAT(src,
|
||||
!HasCall(_A("tmain()::(lambda t20052.cc:80:20)"), _A("C"), "c()"));
|
||||
|
||||
REQUIRE_THAT(src, HasCall(_A("tmain()"), _A("C"), "c()"));
|
||||
REQUIRE_THAT(src, HasCall(_A("C"), _A("C"), "cc()"));
|
||||
REQUIRE_THAT(src, HasCall(_A("C"), _A("C"), "ccc()"));
|
||||
|
||||
REQUIRE_THAT(src,
|
||||
!HasCall(_A("tmain()::(lambda t20052.cc:80:20)"),
|
||||
_A("tmain()::(lambda t20052.cc:67:20)"), "operator()() const"));
|
||||
|
||||
REQUIRE_THAT(src, HasCall(_A("C"), _A("C"), "ccc()"));
|
||||
|
||||
REQUIRE_THAT(src,
|
||||
HasCall(_A("tmain()"), _A("R<(lambda at t20052.cc:86:9)>"),
|
||||
"R((lambda at t20052.cc:86:9) &&)"));
|
||||
REQUIRE_THAT(src,
|
||||
HasCall(_A("tmain()"), _A("R<(lambda at t20052.cc:86:9)>"), "r()"));
|
||||
REQUIRE_THAT(src,
|
||||
!HasCall(_A("R<(lambda at t20052.cc:86:9)>"),
|
||||
_A("tmain()::(lambda t20052.cc:86:9)"), "operator()() const"));
|
||||
|
||||
REQUIRE_THAT(
|
||||
src, HasCall(_A("R<(lambda at t20052.cc:86:9)>"), _A("C"), "c()"));
|
||||
|
||||
REQUIRE_THAT(src,
|
||||
!HasCall(_A("tmain()"), _A("tmain()::(lambda t20052.cc:94:9)"),
|
||||
"operator()(auto) const"));
|
||||
REQUIRE_THAT(src,
|
||||
!HasCall(_A("tmain()::(lambda t20052.cc:94:9)"), _A("D"),
|
||||
"add5(int) const"));
|
||||
|
||||
REQUIRE_THAT(src, HasCall(_A("tmain()"), _A("D"), "add5(int) const"));
|
||||
|
||||
save_puml(config.output_directory(), diagram->name + ".puml", src);
|
||||
}
|
||||
|
||||
{
|
||||
auto j = generate_sequence_json(diagram, *model);
|
||||
|
||||
using namespace json;
|
||||
|
||||
std::vector<int> messages = {
|
||||
FindMessage(j, "tmain()", "A", "a()"),
|
||||
FindMessage(j, "A", "A", "aa()"),
|
||||
FindMessage(j, "A", "A", "aaa()"),
|
||||
FindMessage(j, "tmain()", "B", "b()"),
|
||||
FindMessage(j, "B", "B", "bb()"),
|
||||
FindMessage(j, "B", "B", "bbb()"),
|
||||
FindMessage(j, "tmain()", "C", "c()"),
|
||||
FindMessage(j, "C", "C", "cc()"),
|
||||
FindMessage(j, "C", "C", "ccc()"),
|
||||
FindMessage(j, "tmain()", "R<(lambda at t20052.cc:86:9)>", "r()"),
|
||||
FindMessage(j, "R<(lambda at t20052.cc:86:9)>", "C", "c()"),
|
||||
};
|
||||
|
||||
REQUIRE(std::is_sorted(messages.begin(), messages.end()));
|
||||
|
||||
save_json(config.output_directory(), diagram->name + ".json", j);
|
||||
}
|
||||
|
||||
{
|
||||
auto src = generate_sequence_mermaid(diagram, *model);
|
||||
|
||||
mermaid::SequenceDiagramAliasMatcher _A(src);
|
||||
using mermaid::HasCall;
|
||||
|
||||
REQUIRE_THAT(src,
|
||||
!HasCall(_A("tmain()"), _A("tmain()::(lambda t20052.cc:67:20)"),
|
||||
"operator()() const"));
|
||||
REQUIRE_THAT(src,
|
||||
!HasCall(_A("tmain()::(lambda t20052.cc:67:20)"), _A("A"), "a()"));
|
||||
|
||||
REQUIRE_THAT(src, HasCall(_A("tmain()"), _A("A"), "a()"));
|
||||
REQUIRE_THAT(src, HasCall(_A("A"), _A("A"), "aa()"));
|
||||
REQUIRE_THAT(src, HasCall(_A("A"), _A("A"), "aaa()"));
|
||||
|
||||
REQUIRE_THAT(src,
|
||||
!HasCall(_A("tmain()::(lambda t20052.cc:67:20)"), _A("B"), "b()"));
|
||||
|
||||
REQUIRE_THAT(src, HasCall(_A("tmain()"), _A("B"), "b()"));
|
||||
REQUIRE_THAT(src, HasCall(_A("B"), _A("B"), "bb()"));
|
||||
REQUIRE_THAT(src, HasCall(_A("B"), _A("B"), "bbb()"));
|
||||
|
||||
REQUIRE_THAT(src,
|
||||
!HasCall(_A("tmain()::(lambda t20052.cc:80:20)"), _A("C"), "c()"));
|
||||
|
||||
REQUIRE_THAT(src, HasCall(_A("tmain()"), _A("C"), "c()"));
|
||||
REQUIRE_THAT(src, HasCall(_A("C"), _A("C"), "cc()"));
|
||||
REQUIRE_THAT(src, HasCall(_A("C"), _A("C"), "ccc()"));
|
||||
|
||||
REQUIRE_THAT(src,
|
||||
!HasCall(_A("tmain()::(lambda t20052.cc:80:20)"),
|
||||
_A("tmain()::(lambda t20052.cc:67:20)"), "operator()() const"));
|
||||
|
||||
REQUIRE_THAT(src, HasCall(_A("C"), _A("C"), "ccc()"));
|
||||
|
||||
REQUIRE_THAT(src,
|
||||
HasCall(_A("tmain()"), _A("R<(lambda at t20052.cc:86:9)>"),
|
||||
"R((lambda at t20052.cc:86:9) &&)"));
|
||||
REQUIRE_THAT(src,
|
||||
HasCall(_A("tmain()"), _A("R<(lambda at t20052.cc:86:9)>"), "r()"));
|
||||
REQUIRE_THAT(src,
|
||||
!HasCall(_A("R<(lambda at t20052.cc:86:9)>"),
|
||||
_A("tmain()::(lambda t20052.cc:86:9)"), "operator()() const"));
|
||||
|
||||
REQUIRE_THAT(
|
||||
src, HasCall(_A("R<(lambda at t20052.cc:86:9)>"), _A("C"), "c()"));
|
||||
|
||||
REQUIRE_THAT(src,
|
||||
!HasCall(_A("tmain()"), _A("tmain()::(lambda t20052.cc:94:9)"),
|
||||
"operator()(auto) const"));
|
||||
REQUIRE_THAT(src,
|
||||
!HasCall(_A("tmain()::(lambda t20052.cc:94:9)"), _A("D"),
|
||||
"add5(int) const"));
|
||||
|
||||
REQUIRE_THAT(src, HasCall(_A("tmain()"), _A("D"), "add5(int) const"));
|
||||
|
||||
save_mermaid(config.output_directory(), diagram->name + ".mmd", src);
|
||||
}
|
||||
}
|
||||
@@ -480,6 +480,7 @@ using namespace clanguml::test::matchers;
|
||||
#include "t20050/test_case.h"
|
||||
#include "t20051/test_case.h"
|
||||
#endif
|
||||
#include "t20052/test_case.h"
|
||||
|
||||
///
|
||||
/// Package diagram tests
|
||||
|
||||
@@ -376,6 +376,9 @@ test_cases:
|
||||
- name: t20051
|
||||
title: Test case for CUDA calls callee_type filter
|
||||
description:
|
||||
- name: t20052
|
||||
title: Test case for CUDA calls callee_type filter
|
||||
description:
|
||||
Package diagrams:
|
||||
- name: t30001
|
||||
title: Basic package diagram test case
|
||||
|
||||
Reference in New Issue
Block a user