Fixed else if statement generation in sequence diagrams (Fixes #81)

This commit is contained in:
Bartek Kryza
2023-08-06 21:38:33 +02:00
parent 39be6014ed
commit fd06d93afb
8 changed files with 44 additions and 24 deletions

View File

@@ -1,5 +1,6 @@
# CHANGELOG # CHANGELOG
* Fixed 'else if' statement generation in sequence diagrams (#81)
* Implemented removal of redundant dependency relationhips (#28) * Implemented removal of redundant dependency relationhips (#28)
* Add option to disable generation of dependency relation to template * Add option to disable generation of dependency relation to template
arguments (#141) arguments (#141)

View File

@@ -211,7 +211,7 @@ void generate_diagrams(const std::vector<std::string> &diagram_names,
if (indicator) if (indicator)
indicator->complete(name); indicator->complete(name);
} }
catch (std::runtime_error &e) { catch (std::exception &e) {
if (indicator) if (indicator)
indicator->fail(name); indicator->fail(name);

View File

@@ -181,22 +181,27 @@ void call_expression_context::enter_ifstmt(clang::IfStmt *stmt)
void call_expression_context::leave_ifstmt() void call_expression_context::leave_ifstmt()
{ {
if (!if_stmt_stack_.empty()) { if (!if_stmt_stack_.empty()) {
elseif_stmt_stacks_.erase(current_ifstmt());
if_stmt_stack_.pop(); if_stmt_stack_.pop();
std::stack<clang::IfStmt *>{}.swap(elseif_stmt_stack_);
} }
} }
void call_expression_context::enter_elseifstmt(clang::IfStmt *stmt) void call_expression_context::enter_elseifstmt(clang::IfStmt *stmt)
{ {
elseif_stmt_stack_.push(stmt); assert(current_ifstmt() != nullptr);
elseif_stmt_stacks_[current_ifstmt()].push(stmt);
} }
clang::IfStmt *call_expression_context::current_elseifstmt() const clang::IfStmt *call_expression_context::current_elseifstmt() const
{ {
if (elseif_stmt_stack_.empty()) assert(current_ifstmt() != nullptr);
if (elseif_stmt_stacks_.count(current_ifstmt()) == 0 ||
elseif_stmt_stacks_.at(current_ifstmt()).empty())
return nullptr; return nullptr;
return elseif_stmt_stack_.top(); return elseif_stmt_stacks_.at(current_ifstmt()).top();
} }
clang::Stmt *call_expression_context::current_loopstmt() const clang::Stmt *call_expression_context::current_loopstmt() const
@@ -315,11 +320,11 @@ bool call_expression_context::is_expr_in_current_control_statement_condition(
if (common::is_subexpr_of(condition_decl_stmt, stmt)) 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 (current_conditionaloperator() != nullptr) {

View File

@@ -321,7 +321,7 @@ private:
std::stack<callexpr_stack_t> call_expr_stack_; std::stack<callexpr_stack_t> call_expr_stack_;
std::stack<clang::IfStmt *> if_stmt_stack_; std::stack<clang::IfStmt *> if_stmt_stack_;
std::stack<clang::IfStmt *> elseif_stmt_stack_; std::map<clang::IfStmt *, std::stack<clang::IfStmt *>> elseif_stmt_stacks_;
std::stack<clang::Stmt *> loop_stmt_stack_; std::stack<clang::Stmt *> loop_stmt_stack_;
std::stack<clang::Stmt *> try_stmt_stack_; std::stack<clang::Stmt *> try_stmt_stack_;

View File

@@ -539,7 +539,8 @@ bool translation_unit_visitor::TraverseCompoundStmt(clang::CompoundStmt *stmt)
return true; return true;
const auto *current_ifstmt = context().current_ifstmt(); const auto *current_ifstmt = context().current_ifstmt();
const auto *current_elseifstmt = context().current_elseifstmt(); const auto *current_elseifstmt =
current_ifstmt != nullptr ? context().current_elseifstmt() : nullptr;
// //
// Add final else block (not else if) // Add final else block (not else if)
@@ -583,6 +584,8 @@ 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();
const auto *current_elseifstmt =
current_ifstmt != nullptr ? context().current_elseifstmt() : nullptr;
std::string condition_text; std::string condition_text;
if (config().generate_condition_statements()) if (config().generate_condition_statements())
@@ -590,14 +593,18 @@ bool translation_unit_visitor::TraverseIfStmt(clang::IfStmt *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) { auto child_stmt_compare = [stmt](auto *child_stmt) {
for (const auto *child_stmt : current_ifstmt->children()) { return child_stmt == stmt;
if (child_stmt == stmt) { };
elseif_block = true;
break; if (current_ifstmt != nullptr)
} elseif_block = elseif_block ||
} std::any_of(current_ifstmt->children().begin(),
} current_ifstmt->children().end(), child_stmt_compare);
if (current_elseifstmt != nullptr)
elseif_block = elseif_block ||
std::any_of(current_elseifstmt->children().begin(),
current_elseifstmt->children().end(), child_stmt_compare);
if ((current_caller_id != 0) && !stmt->isConstexpr()) { if ((current_caller_id != 0) && !stmt->isConstexpr()) {
if (elseif_block) { if (elseif_block) {
@@ -620,10 +627,12 @@ bool translation_unit_visitor::TraverseIfStmt(clang::IfStmt *stmt)
RecursiveASTVisitor<translation_unit_visitor>::TraverseIfStmt(stmt); RecursiveASTVisitor<translation_unit_visitor>::TraverseIfStmt(stmt);
if ((current_caller_id != 0) && !stmt->isConstexpr() && !elseif_block) { if ((current_caller_id != 0) && !stmt->isConstexpr()) {
diagram().end_block_message( if (!elseif_block) {
{message_t::kIfEnd, current_caller_id}, message_t::kIf); diagram().end_block_message(
context().leave_ifstmt(); {message_t::kIfEnd, current_caller_id}, message_t::kIf);
context().leave_ifstmt();
}
} }
return true; return true;

View File

@@ -8,6 +8,7 @@ struct A {
int a2() { return 1; } int a2() { return 1; }
int a3() { return 2; } int a3() { return 2; }
int a4() { return 3; } int a4() { return 3; }
int a5() { return 4; }
}; };
struct B { struct B {
@@ -48,6 +49,9 @@ int tmain()
if (reinterpret_cast<uint64_t>(&a) % 100 == 0ULL) { if (reinterpret_cast<uint64_t>(&a) % 100 == 0ULL) {
result = a.a1(); result = a.a1();
} }
else if (reinterpret_cast<uint64_t>(&a) % 100 == 42ULL) {
result = a.a5();
}
else if (reinterpret_cast<uint64_t>(&a) % 64 == 0ULL) { else if (reinterpret_cast<uint64_t>(&a) % 64 == 0ULL) {
if (c.c3(a.a2()) > 2) if (c.c3(a.a2()) > 2)
result = b.b1(); result = b.b1();

View File

@@ -63,6 +63,7 @@ TEST_CASE("t20020", "[test-case][sequence]")
using namespace json; using namespace json;
std::vector<int> messages = {FindMessage(j, "tmain()", "A", "a1()"), std::vector<int> messages = {FindMessage(j, "tmain()", "A", "a1()"),
FindMessage(j, "tmain()", "A", "a5()"),
FindMessage(j, "tmain()", "A", "a2()"), FindMessage(j, "tmain()", "A", "a2()"),
FindMessage(j, "tmain()", "C", "c3(int)"), FindMessage(j, "tmain()", "C", "c3(int)"),
FindMessage(j, "tmain()", "B", "b1()"), FindMessage(j, "tmain()", "B", "b1()"),

View File

@@ -10,4 +10,4 @@ plantuml:
before: before:
- 'title clang-uml clanguml::class_diagram::generators::plantuml::generator sequence diagram' - 'title clang-uml clanguml::class_diagram::generators::plantuml::generator sequence diagram'
start_from: start_from:
- function: "clanguml::class_diagram::generators::plantuml::generator::generate(std::ostream &) const" - function: "clanguml::common::generators::plantuml::generator<clanguml::config::class_diagram,clanguml::class_diagram::model::diagram>::generate(std::ostream &) const"