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