Added option inline_lambda_messages to omit lambda expressions from sequence diagrams (#261)
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user