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
* Fixed 'else if' statement generation in sequence diagrams (#81)
* Implemented removal of redundant dependency relationhips (#28)
* Add option to disable generation of dependency relation to template
arguments (#141)

View File

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

View File

@@ -181,22 +181,27 @@ void call_expression_context::enter_ifstmt(clang::IfStmt *stmt)
void call_expression_context::leave_ifstmt()
{
if (!if_stmt_stack_.empty()) {
elseif_stmt_stacks_.erase(current_ifstmt());
if_stmt_stack_.pop();
std::stack<clang::IfStmt *>{}.swap(elseif_stmt_stack_);
}
}
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
{
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 elseif_stmt_stack_.top();
return elseif_stmt_stacks_.at(current_ifstmt()).top();
}
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))
return true;
}
}
if (current_elseifstmt() != nullptr) {
if (common::is_subexpr_of(current_elseifstmt()->getCond(), stmt))
return true;
if (current_elseifstmt() != nullptr) {
if (common::is_subexpr_of(current_elseifstmt()->getCond(), stmt))
return true;
}
}
if (current_conditionaloperator() != nullptr) {

View File

@@ -321,7 +321,7 @@ private:
std::stack<callexpr_stack_t> call_expr_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 *> try_stmt_stack_;

View File

@@ -539,7 +539,8 @@ bool translation_unit_visitor::TraverseCompoundStmt(clang::CompoundStmt *stmt)
return true;
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)
@@ -583,6 +584,8 @@ bool translation_unit_visitor::TraverseIfStmt(clang::IfStmt *stmt)
const auto current_caller_id = context().caller_id();
const auto *current_ifstmt = context().current_ifstmt();
const auto *current_elseifstmt =
current_ifstmt != nullptr ? context().current_elseifstmt() : nullptr;
std::string condition_text;
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
// else if condition of the current if statement
if (current_ifstmt != nullptr) {
for (const auto *child_stmt : current_ifstmt->children()) {
if (child_stmt == stmt) {
elseif_block = true;
break;
}
}
}
auto child_stmt_compare = [stmt](auto *child_stmt) {
return child_stmt == stmt;
};
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 (elseif_block) {
@@ -620,10 +627,12 @@ bool translation_unit_visitor::TraverseIfStmt(clang::IfStmt *stmt)
RecursiveASTVisitor<translation_unit_visitor>::TraverseIfStmt(stmt);
if ((current_caller_id != 0) && !stmt->isConstexpr() && !elseif_block) {
diagram().end_block_message(
{message_t::kIfEnd, current_caller_id}, message_t::kIf);
context().leave_ifstmt();
if ((current_caller_id != 0) && !stmt->isConstexpr()) {
if (!elseif_block) {
diagram().end_block_message(
{message_t::kIfEnd, current_caller_id}, message_t::kIf);
context().leave_ifstmt();
}
}
return true;

View File

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

View File

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

View File

@@ -10,4 +10,4 @@ plantuml:
before:
- 'title clang-uml clanguml::class_diagram::generators::plantuml::generator sequence diagram'
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"