Added option to include if and loop condition text in the diagram (fixes #162)
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
* [Lambda expressions in sequence diagrams](#lambda-expressions-in-sequence-diagrams)
|
* [Lambda expressions in sequence diagrams](#lambda-expressions-in-sequence-diagrams)
|
||||||
* [Customizing participants order](#customizing-participants-order)
|
* [Customizing participants order](#customizing-participants-order)
|
||||||
* [Generating return types](#generating-return-types)
|
* [Generating return types](#generating-return-types)
|
||||||
|
* [Generating condition statements](#generating-condition-statements)
|
||||||
|
|
||||||
<!-- tocstop -->
|
<!-- tocstop -->
|
||||||
|
|
||||||
@@ -263,3 +264,15 @@ generate_return_types: true
|
|||||||
This option only affects the `plantuml` generation, in `json` generator
|
This option only affects the `plantuml` generation, in `json` generator
|
||||||
`return_type` property is always present in the message nodes.
|
`return_type` property is always present in the message nodes.
|
||||||
|
|
||||||
|
|
||||||
|
## Generating condition statements
|
||||||
|
Sometimes, it is useful to include actual condition statements (for instance
|
||||||
|
contents of the `if()` condition in the `alt` or `loop` blocks in the sequence
|
||||||
|
diagrams, to make them more readable.
|
||||||
|
|
||||||
|
This can be enabled using the following option:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
generate_condition_statements: true
|
||||||
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -564,6 +564,75 @@ bool is_type_token(const std::string &t)
|
|||||||
(is_identifier(t) && !is_qualifier(t) && !is_bracket(t));
|
(is_identifier(t) && !is_qualifier(t) && !is_bracket(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string format_condition_text(const std::string &condition_text)
|
||||||
|
{
|
||||||
|
std::string result{condition_text};
|
||||||
|
|
||||||
|
if (result.size() < 2)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
std::vector<std::string> text_lines = util::split(result, "\n", true);
|
||||||
|
|
||||||
|
// Trim each line
|
||||||
|
for (auto &line : text_lines) {
|
||||||
|
line = util::trim(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = util::join(" ", text_lines);
|
||||||
|
|
||||||
|
if (result.at(0) == '(' && result.back() == ')')
|
||||||
|
return result.substr(1, result.size() - 2);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_condition_text(clang::SourceManager &sm, clang::IfStmt *stmt)
|
||||||
|
{
|
||||||
|
auto condition_range =
|
||||||
|
clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
|
||||||
|
|
||||||
|
return format_condition_text(get_source_text(condition_range, sm));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_condition_text(clang::SourceManager &sm, clang::WhileStmt *stmt)
|
||||||
|
{
|
||||||
|
auto condition_range =
|
||||||
|
clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
|
||||||
|
|
||||||
|
return format_condition_text(get_source_text(condition_range, sm));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_condition_text(
|
||||||
|
clang::SourceManager &sm, clang::CXXForRangeStmt *stmt)
|
||||||
|
{
|
||||||
|
auto condition_range = stmt->getRangeStmt()->getSourceRange();
|
||||||
|
|
||||||
|
return format_condition_text(get_source_text(condition_range, sm));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_condition_text(clang::SourceManager &sm, clang::ForStmt *stmt)
|
||||||
|
{
|
||||||
|
auto condition_range =
|
||||||
|
clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
|
||||||
|
|
||||||
|
return format_condition_text(get_source_text(condition_range, sm));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_condition_text(clang::SourceManager &sm, clang::DoStmt *stmt)
|
||||||
|
{
|
||||||
|
auto condition_range = stmt->getCond()->getSourceRange();
|
||||||
|
|
||||||
|
return format_condition_text(get_source_text(condition_range, sm));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_condition_text(
|
||||||
|
clang::SourceManager &sm, clang::ConditionalOperator *stmt)
|
||||||
|
{
|
||||||
|
auto condition_range = stmt->getCond()->getSourceRange();
|
||||||
|
|
||||||
|
return format_condition_text(get_source_text(condition_range, sm));
|
||||||
|
}
|
||||||
|
|
||||||
clang::QualType dereference(clang::QualType type)
|
clang::QualType dereference(clang::QualType type)
|
||||||
{
|
{
|
||||||
auto res = type;
|
auto res = type;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "util/util.h"
|
#include "util/util.h"
|
||||||
|
|
||||||
|
#include <clang/AST/Expr.h>
|
||||||
#include <clang/AST/RecursiveASTVisitor.h>
|
#include <clang/AST/RecursiveASTVisitor.h>
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
@@ -248,6 +249,23 @@ bool is_qualified_identifier(const std::string &t);
|
|||||||
|
|
||||||
bool is_type_token(const std::string &t);
|
bool is_type_token(const std::string &t);
|
||||||
|
|
||||||
|
std::string format_condition_text(const std::string &condition_text);
|
||||||
|
|
||||||
|
std::string get_condition_text(clang::SourceManager &sm, clang::IfStmt *stmt);
|
||||||
|
|
||||||
|
std::string get_condition_text(
|
||||||
|
clang::SourceManager &sm, clang::WhileStmt *stmt);
|
||||||
|
|
||||||
|
std::string get_condition_text(
|
||||||
|
clang::SourceManager &sm, clang::CXXForRangeStmt *stmt);
|
||||||
|
|
||||||
|
std::string get_condition_text(clang::SourceManager &sm, clang::ForStmt *stmt);
|
||||||
|
|
||||||
|
std::string get_condition_text(clang::SourceManager &sm, clang::DoStmt *stmt);
|
||||||
|
|
||||||
|
std::string get_condition_text(
|
||||||
|
clang::SourceManager &sm, clang::ConditionalOperator *stmt);
|
||||||
|
|
||||||
clang::QualType dereference(clang::QualType type);
|
clang::QualType dereference(clang::QualType type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -168,6 +168,8 @@ void inheritable_diagram_options::inherit(
|
|||||||
combine_free_functions_into_file_participants.override(
|
combine_free_functions_into_file_participants.override(
|
||||||
parent.combine_free_functions_into_file_participants);
|
parent.combine_free_functions_into_file_participants);
|
||||||
generate_return_types.override(parent.generate_return_types);
|
generate_return_types.override(parent.generate_return_types);
|
||||||
|
generate_condition_statements.override(
|
||||||
|
parent.generate_condition_statements);
|
||||||
debug_mode.override(parent.debug_mode);
|
debug_mode.override(parent.debug_mode);
|
||||||
generate_metadata.override(parent.generate_metadata);
|
generate_metadata.override(parent.generate_metadata);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -446,6 +446,8 @@ struct inheritable_diagram_options {
|
|||||||
option<bool> combine_free_functions_into_file_participants{
|
option<bool> combine_free_functions_into_file_participants{
|
||||||
"combine_free_functions_into_file_participants", false};
|
"combine_free_functions_into_file_participants", false};
|
||||||
option<bool> generate_return_types{"generate_return_types", false};
|
option<bool> generate_return_types{"generate_return_types", false};
|
||||||
|
option<bool> generate_condition_statements{
|
||||||
|
"generate_condition_statements", false};
|
||||||
option<std::vector<std::string>> participants_order{"participants_order"};
|
option<std::vector<std::string>> participants_order{"participants_order"};
|
||||||
option<bool> debug_mode{"debug_mode", false};
|
option<bool> debug_mode{"debug_mode", false};
|
||||||
option<bool> generate_metadata{"generate_metadata", true};
|
option<bool> generate_metadata{"generate_metadata", true};
|
||||||
|
|||||||
@@ -568,6 +568,7 @@ template <> struct convert<sequence_diagram> {
|
|||||||
get_option(node, rhs.start_from);
|
get_option(node, rhs.start_from);
|
||||||
get_option(node, rhs.combine_free_functions_into_file_participants);
|
get_option(node, rhs.combine_free_functions_into_file_participants);
|
||||||
get_option(node, rhs.generate_return_types);
|
get_option(node, rhs.generate_return_types);
|
||||||
|
get_option(node, rhs.generate_condition_statements);
|
||||||
get_option(node, rhs.relative_to);
|
get_option(node, rhs.relative_to);
|
||||||
get_option(node, rhs.participants_order);
|
get_option(node, rhs.participants_order);
|
||||||
get_option(node, rhs.generate_method_arguments);
|
get_option(node, rhs.generate_method_arguments);
|
||||||
@@ -756,6 +757,7 @@ template <> struct convert<config> {
|
|||||||
get_option(node, rhs.generate_metadata);
|
get_option(node, rhs.generate_metadata);
|
||||||
get_option(node, rhs.combine_free_functions_into_file_participants);
|
get_option(node, rhs.combine_free_functions_into_file_participants);
|
||||||
get_option(node, rhs.generate_return_types);
|
get_option(node, rhs.generate_return_types);
|
||||||
|
get_option(node, rhs.generate_condition_statements);
|
||||||
|
|
||||||
rhs.base_directory.set(node["__parent_path"].as<std::string>());
|
rhs.base_directory.set(node["__parent_path"].as<std::string>());
|
||||||
get_option(node, rhs.relative_to);
|
get_option(node, rhs.relative_to);
|
||||||
|
|||||||
@@ -290,6 +290,7 @@ YAML::Emitter &operator<<(
|
|||||||
out << c.comment_parser;
|
out << c.comment_parser;
|
||||||
out << c.combine_free_functions_into_file_participants;
|
out << c.combine_free_functions_into_file_participants;
|
||||||
out << c.generate_return_types;
|
out << c.generate_return_types;
|
||||||
|
out << c.generate_condition_statements;
|
||||||
out << c.participants_order;
|
out << c.participants_order;
|
||||||
out << c.debug_mode;
|
out << c.debug_mode;
|
||||||
|
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ void generator::generate_activity(const activity &a,
|
|||||||
process_conditional_message(m);
|
process_conditional_message(m);
|
||||||
break;
|
break;
|
||||||
case message_t::kConditionalElse:
|
case message_t::kConditionalElse:
|
||||||
process_conditional_else_message();
|
process_conditional_else_message(m);
|
||||||
break;
|
break;
|
||||||
case message_t::kConditionalEnd:
|
case message_t::kConditionalEnd:
|
||||||
process_end_conditional_message();
|
process_end_conditional_message();
|
||||||
@@ -279,6 +279,8 @@ void generator::process_while_message(const message &m) const
|
|||||||
while_block["type"] = "loop";
|
while_block["type"] = "loop";
|
||||||
while_block["name"] = "while";
|
while_block["name"] = "while";
|
||||||
while_block["activity_id"] = std::to_string(m.from());
|
while_block["activity_id"] = std::to_string(m.from());
|
||||||
|
if (auto text = m.condition_text(); text.has_value())
|
||||||
|
while_block["condition_text"] = *text;
|
||||||
|
|
||||||
current_block_statement()["messages"].push_back(std::move(while_block));
|
current_block_statement()["messages"].push_back(std::move(while_block));
|
||||||
|
|
||||||
@@ -298,6 +300,8 @@ void generator::process_for_message(const message &m) const
|
|||||||
for_block["type"] = "loop";
|
for_block["type"] = "loop";
|
||||||
for_block["name"] = "for";
|
for_block["name"] = "for";
|
||||||
for_block["activity_id"] = std::to_string(m.from());
|
for_block["activity_id"] = std::to_string(m.from());
|
||||||
|
if (auto text = m.condition_text(); text.has_value())
|
||||||
|
for_block["condition_text"] = *text;
|
||||||
|
|
||||||
current_block_statement()["messages"].push_back(std::move(for_block));
|
current_block_statement()["messages"].push_back(std::move(for_block));
|
||||||
|
|
||||||
@@ -317,6 +321,8 @@ void generator::process_do_message(const message &m) const
|
|||||||
do_block["type"] = "loop";
|
do_block["type"] = "loop";
|
||||||
do_block["name"] = "do";
|
do_block["name"] = "do";
|
||||||
do_block["activity_id"] = std::to_string(m.from());
|
do_block["activity_id"] = std::to_string(m.from());
|
||||||
|
if (auto text = m.condition_text(); text.has_value())
|
||||||
|
do_block["condition_text"] = *text;
|
||||||
|
|
||||||
current_block_statement()["messages"].push_back(std::move(do_block));
|
current_block_statement()["messages"].push_back(std::move(do_block));
|
||||||
|
|
||||||
@@ -413,6 +419,8 @@ void generator::process_conditional_message(const message &m) const
|
|||||||
if_block["type"] = "alt";
|
if_block["type"] = "alt";
|
||||||
if_block["name"] = "conditional";
|
if_block["name"] = "conditional";
|
||||||
if_block["activity_id"] = std::to_string(m.from());
|
if_block["activity_id"] = std::to_string(m.from());
|
||||||
|
if (auto text = m.condition_text(); text.has_value())
|
||||||
|
if_block["condition_text"] = *text;
|
||||||
|
|
||||||
current_block_statement()["messages"].push_back(std::move(if_block));
|
current_block_statement()["messages"].push_back(std::move(if_block));
|
||||||
|
|
||||||
@@ -427,13 +435,15 @@ void generator::process_conditional_message(const message &m) const
|
|||||||
std::ref(current_block_statement()["branches"].back()));
|
std::ref(current_block_statement()["branches"].back()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void generator::process_conditional_else_message() const
|
void generator::process_conditional_else_message(const message &m) const
|
||||||
{
|
{
|
||||||
// remove previous branch from the stack
|
// remove previous branch from the stack
|
||||||
block_statements_stack_.pop_back();
|
block_statements_stack_.pop_back();
|
||||||
|
|
||||||
nlohmann::json branch;
|
nlohmann::json branch;
|
||||||
branch["type"] = "alternative";
|
branch["type"] = "alternative";
|
||||||
|
if (auto text = m.condition_text(); text.has_value())
|
||||||
|
branch["condition_text"] = *text;
|
||||||
current_block_statement()["branches"].push_back(std::move(branch));
|
current_block_statement()["branches"].push_back(std::move(branch));
|
||||||
|
|
||||||
block_statements_stack_.push_back(
|
block_statements_stack_.push_back(
|
||||||
@@ -477,6 +487,8 @@ void generator::process_if_message(const message &m) const
|
|||||||
if_block["type"] = "alt";
|
if_block["type"] = "alt";
|
||||||
if_block["name"] = "if";
|
if_block["name"] = "if";
|
||||||
if_block["activity_id"] = std::to_string(m.from());
|
if_block["activity_id"] = std::to_string(m.from());
|
||||||
|
if (auto text = m.condition_text(); text.has_value())
|
||||||
|
if_block["condition_text"] = *text;
|
||||||
|
|
||||||
current_block_statement()["messages"].push_back(std::move(if_block));
|
current_block_statement()["messages"].push_back(std::move(if_block));
|
||||||
|
|
||||||
|
|||||||
@@ -158,8 +158,10 @@ private:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Process conditional else statement message
|
* @brief Process conditional else statement message
|
||||||
|
*
|
||||||
|
* @param m Message model
|
||||||
*/
|
*/
|
||||||
void process_conditional_else_message() const;
|
void process_conditional_else_message(const model::message &m) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Process `switch` statement message
|
* @brief Process `switch` statement message
|
||||||
|
|||||||
@@ -178,11 +178,17 @@ void generator::generate_activity(const activity &a, std::ostream &ostr,
|
|||||||
}
|
}
|
||||||
else if (m.type() == message_t::kIf) {
|
else if (m.type() == message_t::kIf) {
|
||||||
print_debug(m, ostr);
|
print_debug(m, ostr);
|
||||||
ostr << "alt\n";
|
ostr << "alt";
|
||||||
|
if (m.condition_text())
|
||||||
|
ostr << " " << m.condition_text().value();
|
||||||
|
ostr << '\n';
|
||||||
}
|
}
|
||||||
else if (m.type() == message_t::kElseIf) {
|
else if (m.type() == message_t::kElseIf) {
|
||||||
print_debug(m, ostr);
|
print_debug(m, ostr);
|
||||||
ostr << "else\n";
|
ostr << "else";
|
||||||
|
if (m.condition_text())
|
||||||
|
ostr << " " << m.condition_text().value();
|
||||||
|
ostr << '\n';
|
||||||
}
|
}
|
||||||
else if (m.type() == message_t::kElse) {
|
else if (m.type() == message_t::kElse) {
|
||||||
print_debug(m, ostr);
|
print_debug(m, ostr);
|
||||||
@@ -193,21 +199,30 @@ void generator::generate_activity(const activity &a, std::ostream &ostr,
|
|||||||
}
|
}
|
||||||
else if (m.type() == message_t::kWhile) {
|
else if (m.type() == message_t::kWhile) {
|
||||||
print_debug(m, ostr);
|
print_debug(m, ostr);
|
||||||
ostr << "loop\n";
|
ostr << "loop";
|
||||||
|
if (m.condition_text())
|
||||||
|
ostr << " " << m.condition_text().value();
|
||||||
|
ostr << '\n';
|
||||||
}
|
}
|
||||||
else if (m.type() == message_t::kWhileEnd) {
|
else if (m.type() == message_t::kWhileEnd) {
|
||||||
ostr << "end\n";
|
ostr << "end\n";
|
||||||
}
|
}
|
||||||
else if (m.type() == message_t::kFor) {
|
else if (m.type() == message_t::kFor) {
|
||||||
print_debug(m, ostr);
|
print_debug(m, ostr);
|
||||||
ostr << "loop\n";
|
ostr << "loop";
|
||||||
|
if (m.condition_text())
|
||||||
|
ostr << " " << m.condition_text().value();
|
||||||
|
ostr << '\n';
|
||||||
}
|
}
|
||||||
else if (m.type() == message_t::kForEnd) {
|
else if (m.type() == message_t::kForEnd) {
|
||||||
ostr << "end\n";
|
ostr << "end\n";
|
||||||
}
|
}
|
||||||
else if (m.type() == message_t::kDo) {
|
else if (m.type() == message_t::kDo) {
|
||||||
print_debug(m, ostr);
|
print_debug(m, ostr);
|
||||||
ostr << "loop\n";
|
ostr << "loop";
|
||||||
|
if (m.condition_text())
|
||||||
|
ostr << " " << m.condition_text().value();
|
||||||
|
ostr << '\n';
|
||||||
}
|
}
|
||||||
else if (m.type() == message_t::kDoEnd) {
|
else if (m.type() == message_t::kDoEnd) {
|
||||||
ostr << "end\n";
|
ostr << "end\n";
|
||||||
@@ -237,7 +252,10 @@ void generator::generate_activity(const activity &a, std::ostream &ostr,
|
|||||||
}
|
}
|
||||||
else if (m.type() == message_t::kConditional) {
|
else if (m.type() == message_t::kConditional) {
|
||||||
print_debug(m, ostr);
|
print_debug(m, ostr);
|
||||||
ostr << "alt\n";
|
ostr << "alt";
|
||||||
|
if (m.condition_text())
|
||||||
|
ostr << " " << m.condition_text().value();
|
||||||
|
ostr << '\n';
|
||||||
}
|
}
|
||||||
else if (m.type() == message_t::kConditionalElse) {
|
else if (m.type() == message_t::kConditionalElse) {
|
||||||
print_debug(m, ostr);
|
print_debug(m, ostr);
|
||||||
|
|||||||
@@ -57,4 +57,17 @@ void message::set_message_scope(common::model::message_scope_t scope)
|
|||||||
|
|
||||||
common::model::message_scope_t message::message_scope() const { return scope_; }
|
common::model::message_scope_t message::message_scope() const { return scope_; }
|
||||||
|
|
||||||
|
void message::condition_text(const std::string &condition_text)
|
||||||
|
{
|
||||||
|
if (condition_text.empty())
|
||||||
|
condition_text_ = std::nullopt;
|
||||||
|
else
|
||||||
|
condition_text_ = condition_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> message::condition_text() const
|
||||||
|
{
|
||||||
|
return condition_text_;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace clanguml::sequence_diagram::model
|
} // namespace clanguml::sequence_diagram::model
|
||||||
|
|||||||
@@ -128,6 +128,20 @@ public:
|
|||||||
*/
|
*/
|
||||||
common::model::message_scope_t message_scope() const;
|
common::model::message_scope_t message_scope() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set condition text for block statements (e.g. if(<THIS TEXT>))
|
||||||
|
*
|
||||||
|
* @param condition_text Condition text
|
||||||
|
*/
|
||||||
|
void condition_text(const std::string &condition_text);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get condition text
|
||||||
|
*
|
||||||
|
* @return Block statement condition text
|
||||||
|
*/
|
||||||
|
std::optional<std::string> condition_text() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
common::model::message_t type_{common::model::message_t::kNone};
|
common::model::message_t type_{common::model::message_t::kNone};
|
||||||
|
|
||||||
@@ -143,6 +157,8 @@ private:
|
|||||||
std::string message_name_{};
|
std::string message_name_{};
|
||||||
|
|
||||||
std::string return_type_{};
|
std::string return_type_{};
|
||||||
|
|
||||||
|
std::optional<std::string> condition_text_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace clanguml::sequence_diagram::model
|
} // namespace clanguml::sequence_diagram::model
|
||||||
|
|||||||
@@ -307,15 +307,25 @@ bool call_expression_context::is_expr_in_current_control_statement_condition(
|
|||||||
const clang::Stmt *stmt) const
|
const clang::Stmt *stmt) const
|
||||||
{
|
{
|
||||||
if (current_ifstmt() != nullptr) {
|
if (current_ifstmt() != nullptr) {
|
||||||
if (common::is_subexpr_of(current_ifstmt()->getCond(), stmt)) {
|
if (common::is_subexpr_of(current_ifstmt()->getCond(), stmt))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (const auto *condition_decl_stmt = current_ifstmt()->getInit();
|
||||||
|
condition_decl_stmt != nullptr) {
|
||||||
|
if (common::is_subexpr_of(condition_decl_stmt, stmt))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_elseifstmt() != nullptr) {
|
if (current_elseifstmt() != nullptr) {
|
||||||
if (common::is_subexpr_of(current_elseifstmt()->getCond(), stmt)) {
|
if (common::is_subexpr_of(current_elseifstmt()->getCond(), stmt))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (current_conditionaloperator() != nullptr) {
|
||||||
|
if (common::is_subexpr_of(
|
||||||
|
current_conditionaloperator()->getCond(), stmt))
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto *loop_stmt = current_loopstmt(); loop_stmt != nullptr) {
|
if (const auto *loop_stmt = current_loopstmt(); loop_stmt != nullptr) {
|
||||||
|
|||||||
@@ -584,6 +584,10 @@ bool translation_unit_visitor::TraverseIfStmt(clang::IfStmt *stmt)
|
|||||||
const auto current_caller_id = context().caller_id();
|
const auto current_caller_id = context().caller_id();
|
||||||
const auto *current_ifstmt = context().current_ifstmt();
|
const auto *current_ifstmt = context().current_ifstmt();
|
||||||
|
|
||||||
|
std::string condition_text;
|
||||||
|
if (config().generate_condition_statements())
|
||||||
|
condition_text = common::get_condition_text(source_manager(), stmt);
|
||||||
|
|
||||||
// Check if this is a beginning of a new if statement, or an
|
// Check if this is a beginning of a new if statement, or an
|
||||||
// else if condition of the current if statement
|
// else if condition of the current if statement
|
||||||
if (current_ifstmt != nullptr) {
|
if (current_ifstmt != nullptr) {
|
||||||
@@ -601,6 +605,7 @@ bool translation_unit_visitor::TraverseIfStmt(clang::IfStmt *stmt)
|
|||||||
|
|
||||||
message m{message_t::kElseIf, current_caller_id};
|
message m{message_t::kElseIf, current_caller_id};
|
||||||
set_source_location(*stmt, m);
|
set_source_location(*stmt, m);
|
||||||
|
m.condition_text(condition_text);
|
||||||
diagram().add_block_message(std::move(m));
|
diagram().add_block_message(std::move(m));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -608,6 +613,7 @@ bool translation_unit_visitor::TraverseIfStmt(clang::IfStmt *stmt)
|
|||||||
|
|
||||||
message m{message_t::kIf, current_caller_id};
|
message m{message_t::kIf, current_caller_id};
|
||||||
set_source_location(*stmt, m);
|
set_source_location(*stmt, m);
|
||||||
|
m.condition_text(condition_text);
|
||||||
diagram().add_block_message(std::move(m));
|
diagram().add_block_message(std::move(m));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -631,10 +637,15 @@ bool translation_unit_visitor::TraverseWhileStmt(clang::WhileStmt *stmt)
|
|||||||
|
|
||||||
const auto current_caller_id = context().caller_id();
|
const auto current_caller_id = context().caller_id();
|
||||||
|
|
||||||
|
std::string condition_text;
|
||||||
|
if (config().generate_condition_statements())
|
||||||
|
condition_text = common::get_condition_text(source_manager(), stmt);
|
||||||
|
|
||||||
if (current_caller_id != 0) {
|
if (current_caller_id != 0) {
|
||||||
context().enter_loopstmt(stmt);
|
context().enter_loopstmt(stmt);
|
||||||
message m{message_t::kWhile, current_caller_id};
|
message m{message_t::kWhile, current_caller_id};
|
||||||
set_source_location(*stmt, m);
|
set_source_location(*stmt, m);
|
||||||
|
m.condition_text(condition_text);
|
||||||
diagram().add_block_message(std::move(m));
|
diagram().add_block_message(std::move(m));
|
||||||
}
|
}
|
||||||
RecursiveASTVisitor<translation_unit_visitor>::TraverseWhileStmt(stmt);
|
RecursiveASTVisitor<translation_unit_visitor>::TraverseWhileStmt(stmt);
|
||||||
@@ -656,10 +667,15 @@ bool translation_unit_visitor::TraverseDoStmt(clang::DoStmt *stmt)
|
|||||||
|
|
||||||
const auto current_caller_id = context().caller_id();
|
const auto current_caller_id = context().caller_id();
|
||||||
|
|
||||||
|
std::string condition_text;
|
||||||
|
if (config().generate_condition_statements())
|
||||||
|
condition_text = common::get_condition_text(source_manager(), stmt);
|
||||||
|
|
||||||
if (current_caller_id != 0) {
|
if (current_caller_id != 0) {
|
||||||
context().enter_loopstmt(stmt);
|
context().enter_loopstmt(stmt);
|
||||||
message m{message_t::kDo, current_caller_id};
|
message m{message_t::kDo, current_caller_id};
|
||||||
set_source_location(*stmt, m);
|
set_source_location(*stmt, m);
|
||||||
|
m.condition_text(condition_text);
|
||||||
diagram().add_block_message(std::move(m));
|
diagram().add_block_message(std::move(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -682,10 +698,15 @@ bool translation_unit_visitor::TraverseForStmt(clang::ForStmt *stmt)
|
|||||||
|
|
||||||
const auto current_caller_id = context().caller_id();
|
const auto current_caller_id = context().caller_id();
|
||||||
|
|
||||||
|
std::string condition_text;
|
||||||
|
if (config().generate_condition_statements())
|
||||||
|
condition_text = common::get_condition_text(source_manager(), stmt);
|
||||||
|
|
||||||
if (current_caller_id != 0) {
|
if (current_caller_id != 0) {
|
||||||
context().enter_loopstmt(stmt);
|
context().enter_loopstmt(stmt);
|
||||||
message m{message_t::kFor, current_caller_id};
|
message m{message_t::kFor, current_caller_id};
|
||||||
set_source_location(*stmt, m);
|
set_source_location(*stmt, m);
|
||||||
|
m.condition_text(condition_text);
|
||||||
diagram().add_block_message(std::move(m));
|
diagram().add_block_message(std::move(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -761,10 +782,15 @@ bool translation_unit_visitor::TraverseCXXForRangeStmt(
|
|||||||
|
|
||||||
const auto current_caller_id = context().caller_id();
|
const auto current_caller_id = context().caller_id();
|
||||||
|
|
||||||
|
std::string condition_text;
|
||||||
|
if (config().generate_condition_statements())
|
||||||
|
condition_text = common::get_condition_text(source_manager(), stmt);
|
||||||
|
|
||||||
if (current_caller_id != 0) {
|
if (current_caller_id != 0) {
|
||||||
context().enter_loopstmt(stmt);
|
context().enter_loopstmt(stmt);
|
||||||
message m{message_t::kFor, current_caller_id};
|
message m{message_t::kFor, current_caller_id};
|
||||||
set_source_location(*stmt, m);
|
set_source_location(*stmt, m);
|
||||||
|
m.condition_text(condition_text);
|
||||||
diagram().add_block_message(std::move(m));
|
diagram().add_block_message(std::move(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -847,10 +873,15 @@ bool translation_unit_visitor::TraverseConditionalOperator(
|
|||||||
|
|
||||||
const auto current_caller_id = context().caller_id();
|
const auto current_caller_id = context().caller_id();
|
||||||
|
|
||||||
|
std::string condition_text;
|
||||||
|
if (config().generate_condition_statements())
|
||||||
|
condition_text = common::get_condition_text(source_manager(), stmt);
|
||||||
|
|
||||||
if (current_caller_id != 0) {
|
if (current_caller_id != 0) {
|
||||||
context().enter_conditionaloperator(stmt);
|
context().enter_conditionaloperator(stmt);
|
||||||
model::message m{message_t::kConditional, current_caller_id};
|
model::message m{message_t::kConditional, current_caller_id};
|
||||||
set_source_location(*stmt, m);
|
set_source_location(*stmt, m);
|
||||||
|
m.condition_text(condition_text);
|
||||||
diagram().add_block_message(std::move(m));
|
diagram().add_block_message(std::move(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ TEST_CASE("t20028", "[test-case][sequence]")
|
|||||||
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
|
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
|
||||||
|
|
||||||
// Check if all calls exist
|
// Check if all calls exist
|
||||||
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("A"), "a()"));
|
REQUIRE_THAT(
|
||||||
|
puml, HasCallInControlCondition(_A("tmain()"), _A("A"), "a()"));
|
||||||
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("A"), "b()"));
|
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("A"), "b()"));
|
||||||
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("A"), "c()"));
|
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("A"), "c()"));
|
||||||
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("A"), "d()"));
|
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("A"), "d()"));
|
||||||
|
|||||||
15
tests/t20033/.clang-uml
Normal file
15
tests/t20033/.clang-uml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
compilation_database_dir: ..
|
||||||
|
output_directory: puml
|
||||||
|
diagrams:
|
||||||
|
t20033_sequence:
|
||||||
|
type: sequence
|
||||||
|
glob:
|
||||||
|
- ../../tests/t20033/t20033.cc
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- clanguml::t20033
|
||||||
|
using_namespace:
|
||||||
|
- clanguml::t20033
|
||||||
|
generate_condition_statements: true
|
||||||
|
start_from:
|
||||||
|
- function: "clanguml::t20033::tmain()"
|
||||||
65
tests/t20033/t20033.cc
Normal file
65
tests/t20033/t20033.cc
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace clanguml {
|
||||||
|
namespace t20033 {
|
||||||
|
struct A {
|
||||||
|
int a1() { return 0; }
|
||||||
|
int a2() { return 1; }
|
||||||
|
int a3() { return 2; }
|
||||||
|
int a4() { return 3; }
|
||||||
|
};
|
||||||
|
|
||||||
|
int tmain()
|
||||||
|
{
|
||||||
|
A a;
|
||||||
|
|
||||||
|
int result{};
|
||||||
|
// clang-format off
|
||||||
|
if(false) {
|
||||||
|
result = 0;
|
||||||
|
}
|
||||||
|
else if (reinterpret_cast<uint64_t>(&a) % 100 == 0ULL) {
|
||||||
|
result = a.a1();
|
||||||
|
}
|
||||||
|
else if (reinterpret_cast<uint64_t>(&a) % 64 == 0ULL) {
|
||||||
|
result = a.a2();
|
||||||
|
}
|
||||||
|
else if(a.a2() == 2 &&
|
||||||
|
a.a3() == 3) {
|
||||||
|
result = a.a3();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result = a.a4();
|
||||||
|
}
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
if (int i = a.a2(); i != 2) {
|
||||||
|
result += a.a3();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < a.a2(); i++) {
|
||||||
|
result += i * a.a3();
|
||||||
|
}
|
||||||
|
|
||||||
|
int retry_count = a.a3();
|
||||||
|
while (retry_count--) {
|
||||||
|
result -= a.a2();
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
result += a.a4();
|
||||||
|
} while (retry_count++ < a.a3());
|
||||||
|
|
||||||
|
result = a.a4() % 6 ? result * 2 : result;
|
||||||
|
|
||||||
|
std::vector<int> ints;
|
||||||
|
for (auto i : ints) {
|
||||||
|
result += a.a4();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
tests/t20033/test_case.h
Normal file
58
tests/t20033/test_case.h
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* tests/t20033/test_case.h
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021-2023 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("t20033", "[test-case][sequence]")
|
||||||
|
{
|
||||||
|
auto [config, db] = load_config("t20033");
|
||||||
|
|
||||||
|
auto diagram = config.diagrams["t20033_sequence"];
|
||||||
|
|
||||||
|
REQUIRE(diagram->name == "t20033_sequence");
|
||||||
|
|
||||||
|
auto model = generate_sequence_diagram(*db, diagram);
|
||||||
|
|
||||||
|
REQUIRE(model->name() == "t20033_sequence");
|
||||||
|
|
||||||
|
{
|
||||||
|
auto puml = generate_sequence_puml(diagram, *model);
|
||||||
|
AliasMatcher _A(puml);
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml, StartsWith("@startuml"));
|
||||||
|
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
|
||||||
|
|
||||||
|
// Check if all calls exist
|
||||||
|
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("A"), "a1()"));
|
||||||
|
REQUIRE_THAT(
|
||||||
|
puml, HasCallInControlCondition(_A("tmain()"), _A("A"), "a2()"));
|
||||||
|
REQUIRE_THAT(
|
||||||
|
puml, HasCallInControlCondition(_A("tmain()"), _A("A"), "a3()"));
|
||||||
|
REQUIRE_THAT(
|
||||||
|
puml, HasCallInControlCondition(_A("tmain()"), _A("A"), "a4()"));
|
||||||
|
|
||||||
|
save_puml(
|
||||||
|
config.output_directory() + "/" + diagram->name + ".puml", puml);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto j = generate_sequence_json(diagram, *model);
|
||||||
|
|
||||||
|
using namespace json;
|
||||||
|
|
||||||
|
save_json(config.output_directory() + "/" + diagram->name + ".json", j);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -346,6 +346,7 @@ using namespace clanguml::test::matchers;
|
|||||||
#include "t20030/test_case.h"
|
#include "t20030/test_case.h"
|
||||||
#include "t20031/test_case.h"
|
#include "t20031/test_case.h"
|
||||||
#include "t20032/test_case.h"
|
#include "t20032/test_case.h"
|
||||||
|
#include "t20033/test_case.h"
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Package diagram tests
|
/// Package diagram tests
|
||||||
|
|||||||
@@ -295,6 +295,9 @@ test_cases:
|
|||||||
- name: t20032
|
- name: t20032
|
||||||
title: Return type generation option sequence diagram test case
|
title: Return type generation option sequence diagram test case
|
||||||
description:
|
description:
|
||||||
|
- name: t20033
|
||||||
|
title: Control statement text in sequence diagram test case
|
||||||
|
description:
|
||||||
Package diagrams:
|
Package diagrams:
|
||||||
- name: t30001
|
- name: t30001
|
||||||
title: Basic package diagram test case
|
title: Basic package diagram test case
|
||||||
|
|||||||
Reference in New Issue
Block a user