diff --git a/CHANGELOG.md b/CHANGELOG.md index 964c1be4..7fd1c255 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/src/common/generators/generators.cc b/src/common/generators/generators.cc index 5ed85c21..582540ac 100644 --- a/src/common/generators/generators.cc +++ b/src/common/generators/generators.cc @@ -211,7 +211,7 @@ void generate_diagrams(const std::vector &diagram_names, if (indicator) indicator->complete(name); } - catch (std::runtime_error &e) { + catch (std::exception &e) { if (indicator) indicator->fail(name); diff --git a/src/sequence_diagram/visitor/call_expression_context.cc b/src/sequence_diagram/visitor/call_expression_context.cc index 025b2d14..e84064a3 100644 --- a/src/sequence_diagram/visitor/call_expression_context.cc +++ b/src/sequence_diagram/visitor/call_expression_context.cc @@ -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{}.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) { diff --git a/src/sequence_diagram/visitor/call_expression_context.h b/src/sequence_diagram/visitor/call_expression_context.h index 05563ab6..41e22647 100644 --- a/src/sequence_diagram/visitor/call_expression_context.h +++ b/src/sequence_diagram/visitor/call_expression_context.h @@ -321,7 +321,7 @@ private: std::stack call_expr_stack_; std::stack if_stmt_stack_; - std::stack elseif_stmt_stack_; + std::map> elseif_stmt_stacks_; std::stack loop_stmt_stack_; std::stack try_stmt_stack_; diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index ac22dc5b..59ea7c6f 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -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::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; diff --git a/tests/t20020/t20020.cc b/tests/t20020/t20020.cc index 7ed55e85..8a19aeeb 100644 --- a/tests/t20020/t20020.cc +++ b/tests/t20020/t20020.cc @@ -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(&a) % 100 == 0ULL) { result = a.a1(); } + else if (reinterpret_cast(&a) % 100 == 42ULL) { + result = a.a5(); + } else if (reinterpret_cast(&a) % 64 == 0ULL) { if (c.c3(a.a2()) > 2) result = b.b1(); diff --git a/tests/t20020/test_case.h b/tests/t20020/test_case.h index 12ebb395..1dcfe826 100644 --- a/tests/t20020/test_case.h +++ b/tests/t20020/test_case.h @@ -63,6 +63,7 @@ TEST_CASE("t20020", "[test-case][sequence]") using namespace json; std::vector 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()"), diff --git a/uml/sequence/class_diagram_generator_sequence.yml b/uml/sequence/class_diagram_generator_sequence.yml index 6bb3c7a7..542f04a5 100644 --- a/uml/sequence/class_diagram_generator_sequence.yml +++ b/uml/sequence/class_diagram_generator_sequence.yml @@ -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" \ No newline at end of file + - function: "clanguml::common::generators::plantuml::generator::generate(std::ostream &) const" \ No newline at end of file