diff --git a/src/common/generators/generators.h b/src/common/generators/generators.h index c8c0c1b2..96d967ce 100644 --- a/src/common/generators/generators.h +++ b/src/common/generators/generators.h @@ -359,6 +359,8 @@ std::unique_ptr generate(const common::compilation_database &db, diagram->set_complete(true); + diagram->finalize(); + return diagram; } diff --git a/src/common/model/diagram.cc b/src/common/model/diagram.cc index 907a3902..22f8e290 100644 --- a/src/common/model/diagram.cc +++ b/src/common/model/diagram.cc @@ -59,6 +59,8 @@ void diagram::set_complete(bool complete) { complete_ = complete; } bool diagram::complete() const { return complete_; } +void diagram::finalize() { } + bool diagram::should_include(const element &e) const { if (filter_.get() == nullptr) diff --git a/src/common/model/diagram.h b/src/common/model/diagram.h index ae78914a..61b10bde 100644 --- a/src/common/model/diagram.h +++ b/src/common/model/diagram.h @@ -124,12 +124,24 @@ public: void set_complete(bool complete); /** - * Whether the diagram is complete. + * @brief Whether the diagram is complete. + * + * This flag is set to true, when all translation units for this diagram + * have been visited. * * @return Diagram completion status. */ bool complete() const; + /** + * @brief Once the diagram is complete, run any final processing. + * + * This method should be overriden by specific diagram models to do some + * final tasks like cleaning up the model (e.g. some filters only work + * on completed diagrams). + */ + virtual void finalize(); + // TODO: refactor to a template method bool should_include(const element &e) const; bool should_include(const namespace_ &ns) const; diff --git a/src/common/model/diagram_filter.cc b/src/common/model/diagram_filter.cc index 98a03cf9..560a427a 100644 --- a/src/common/model/diagram_filter.cc +++ b/src/common/model/diagram_filter.cc @@ -376,6 +376,7 @@ tvl::value_t callee_filter::match( const diagram &d, const sequence_diagram::model::participant &p) const { using sequence_diagram::model::class_; + using sequence_diagram::model::function; using sequence_diagram::model::method; using sequence_diagram::model::participant; @@ -391,6 +392,10 @@ tvl::value_t callee_filter::match( tvl::value_t res = tvl::any_of( callee_types_.begin(), callee_types_.end(), [&p, is_lambda](auto ct) { + auto is_function = [](const participant *p) { + return dynamic_cast(p) != nullptr; + }; + switch (ct) { case config::callee_type::method: return p.type_name() == "method"; @@ -401,12 +406,12 @@ tvl::value_t callee_filter::match( return p.type_name() == "method" && ((method &)p).is_assignment(); case config::callee_type::operator_: - return p.type_name() == "method" && ((method &)p).is_operator(); + return is_function(&p) && ((function &)p).is_operator(); case config::callee_type::defaulted: return p.type_name() == "method" && ((method &)p).is_defaulted(); case config::callee_type::static_: - return p.type_name() == "method" && ((method &)p).is_static(); + return is_function(&p) && ((function &)p).is_static(); case config::callee_type::function: return p.type_name() == "function"; case config::callee_type::function_template: diff --git a/src/sequence_diagram/generators/json/sequence_diagram_generator.cc b/src/sequence_diagram/generators/json/sequence_diagram_generator.cc index 1c0e7d84..33f298cb 100644 --- a/src/sequence_diagram/generators/json/sequence_diagram_generator.cc +++ b/src/sequence_diagram/generators/json/sequence_diagram_generator.cc @@ -252,16 +252,6 @@ nlohmann::json &generator::current_block_statement() const void generator::process_call_message(const model::message &m, std::vector &visited) const { - const auto &to = m_model.get_participant(m.to()); - - if (!to || to.value().skip()) - return; - - if (!m_model.should_include(to.value())) { - LOG_DBG("Excluding call from '{}' to '{}'", m.from(), m.to()); - return; - } - visited.push_back(m.from()); LOG_DBG("Generating message {} --> {}", m.from(), m.to()); diff --git a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc index 3d59ac90..03b31fb4 100644 --- a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc +++ b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc @@ -139,14 +139,6 @@ void generator::generate_activity(const activity &a, std::ostream &ostr, if (m.type() == message_t::kCall) { const auto &to = m_model.get_participant(m.to()); - if (!to || to.value().skip()) - continue; - - if (!m_model.should_include(to.value())) { - LOG_DBG("Excluding call from [{}] to {} [{}]", m.from(), - to.value().full_name(false), m.to()); - continue; - } visited.push_back(m.from()); diff --git a/src/sequence_diagram/model/diagram.cc b/src/sequence_diagram/model/diagram.cc index b009d9f2..9cb1ed81 100644 --- a/src/sequence_diagram/model/diagram.cc +++ b/src/sequence_diagram/model/diagram.cc @@ -248,6 +248,81 @@ void diagram::fold_or_end_block_statement(message &&m, current_messages.emplace_back(std::move(m)); } } + +void diagram::finalize() +{ + // Apply diagram filters and remove any empty block statements + using common::model::message_t; + + // First in each sequence (activity) filter out any remaining + // uninteresting calls + for (auto &[id, act] : sequences_) { + util::erase_if(act.messages(), [this](auto &m) { + if (m.type() != message_t::kCall) + return false; + + const auto &to = get_participant(m.to()); + if (!to || to.value().skip()) + return true; + + if (!should_include(to.value())) { + LOG_DBG("Excluding call from [{}] to {} [{}]", m.from(), + to.value().full_name(false), m.to()); + return true; + } + + return false; + }); + } + + // Now remove any empty block statements, e.g. if/endif + for (auto &[id, act] : sequences_) { + int64_t block_nest_level{0}; + std::vector> block_message_stack; + // Add first stack level - this level will contain the filtered + // message sequence + block_message_stack.emplace_back(); + + // First create a recursive stack from the messages and + // message blocks (e.g. if statements) + for (auto &m : act.messages()) { + if (is_begin_block_message(m.type())) { + block_nest_level++; + block_message_stack.push_back({m}); + } + else if (is_end_block_message(m.type())) { + block_nest_level--; + + block_message_stack.back().push_back(m); + + // Check the last stack for any calls, if yes, collapse it + // on the previous stack + if (std::count_if(block_message_stack.back().begin(), + block_message_stack.back().end(), [](auto &m) { + return m.type() == message_t::kCall; + }) > 0) { + std::copy(block_message_stack.back().begin(), + block_message_stack.back().end(), + std::back_inserter( + block_message_stack.at(block_nest_level))); + } + + block_message_stack.pop_back(); + + assert(block_nest_level >= 0); + } + else { + block_message_stack.back().push_back(m); + } + } + + act.messages().clear(); + + for (auto &m : block_message_stack[0]) { + act.add_message(m); + } + } +} } // namespace clanguml::sequence_diagram::model namespace clanguml::common::model { diff --git a/src/sequence_diagram/model/diagram.h b/src/sequence_diagram/model/diagram.h index 78ca2fdf..d5bf0f20 100644 --- a/src/sequence_diagram/model/diagram.h +++ b/src/sequence_diagram/model/diagram.h @@ -212,6 +212,15 @@ public: */ bool should_include(const sequence_diagram::model::participant &p) const; + /** + * @brief Once the diagram is complete, run any final processing. + * + * This method should be overriden by specific diagram models to do some + * final tasks like cleaning up the model (e.g. some filters only work + * on completed diagrams). + */ + void finalize() override; + private: /** * This method checks the last messages in sequence (current_messages), @@ -233,6 +242,27 @@ private: common::model::message_t statement_begin, std::vector ¤t_messages) const; + bool is_begin_block_message(common::model::message_t mt) + { + using common::model::message_t; + static std::set block_begin_types{message_t::kIf, + message_t::kWhile, message_t::kDo, message_t::kFor, message_t::kTry, + message_t::kSwitch, message_t::kConditional}; + + return block_begin_types.count(mt) > 0; + }; + + bool is_end_block_message(common::model::message_t mt) + { + using common::model::message_t; + static std::set block_end_types{message_t::kIfEnd, + message_t::kWhileEnd, message_t::kDoEnd, message_t::kForEnd, + message_t::kTryEnd, message_t::kSwitchEnd, + message_t::kConditionalEnd}; + + return block_end_types.count(mt) > 0; + }; + bool started_{false}; std::map sequences_; diff --git a/src/sequence_diagram/model/participant.cc b/src/sequence_diagram/model/participant.cc index f189bcd9..3772b891 100644 --- a/src/sequence_diagram/model/participant.cc +++ b/src/sequence_diagram/model/participant.cc @@ -143,6 +143,10 @@ bool function::is_static() const { return is_static_; } void function::is_static(bool s) { is_static_ = s; } +bool function::is_operator() const { return is_operator_; } + +void function::is_operator(bool o) { is_operator_ = o; } + void function::add_parameter(const std::string &a) { parameters_.push_back(a); } const std::vector &function::parameters() const @@ -176,10 +180,6 @@ bool method::is_assignment() const { return is_assignment_; } void method::is_assignment(bool a) { is_assignment_ = a; } -bool method::is_operator() const { return is_operator_; } - -void method::is_operator(bool o) { is_operator_ = o; } - void method::set_method_name(const std::string &name) { method_name_ = name; } void method::set_class_id(diagram_element::id_t id) { class_id_ = id; } diff --git a/src/sequence_diagram/model/participant.h b/src/sequence_diagram/model/participant.h index f7d710cd..7cf80cd1 100644 --- a/src/sequence_diagram/model/participant.h +++ b/src/sequence_diagram/model/participant.h @@ -277,6 +277,20 @@ struct function : public participant { */ void is_static(bool s); + /** + * @brief Check, if the method is an operator + * + * @return True, if the method is an operator + */ + bool is_operator() const; + + /** + * @brief Set whether the method is an operator + * + * @param v True, if the method is an operator + */ + void is_operator(bool o); + /** * @brief Add a function parameter * @@ -298,6 +312,7 @@ private: bool is_const_{false}; bool is_void_{false}; bool is_static_{false}; + bool is_operator_{false}; std::vector parameters_; }; @@ -429,20 +444,6 @@ struct method : public function { */ void is_assignment(bool a); - /** - * @brief Check, if the method is an operator - * - * @return True, if the method is an operator - */ - bool is_operator() const; - - /** - * @brief Set whether the method is an operator - * - * @param v True, if the method is an operator - */ - void is_operator(bool o); - private: diagram_element::id_t class_id_{}; std::string method_name_; @@ -450,7 +451,6 @@ private: bool is_constructor_{false}; bool is_defaulted_{false}; bool is_assignment_{false}; - bool is_operator_{false}; }; /** diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index 4136e698..c295bfea 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -330,6 +330,8 @@ bool translation_unit_visitor::VisitFunctionDecl( function_model_ptr->is_void(declaration->getReturnType()->isVoidType()); + function_model_ptr->is_operator(declaration->isOverloadedOperator()); + context().update(declaration); context().set_caller_id(function_model_ptr->id()); @@ -373,6 +375,9 @@ bool translation_unit_visitor::VisitFunctionTemplateDecl( function_template_model->set_id( common::to_id(function_template_model->full_name(false))); + function_template_model->is_operator( + declaration->getAsFunction()->isOverloadedOperator()); + context().update(declaration); context().set_caller_id(function_template_model->id()); @@ -2260,6 +2265,7 @@ void translation_unit_visitor::finalize() { std::set active_participants_unique; + // Change all active participants AST local ids to diagram global ids for (auto id : diagram().active_participants()) { if (local_ast_id_map_.find(id) != local_ast_id_map_.end()) { active_participants_unique.emplace(local_ast_id_map_.at(id)); @@ -2271,6 +2277,7 @@ void translation_unit_visitor::finalize() diagram().active_participants() = std::move(active_participants_unique); + // Change all message callees AST local ids to diagram global ids for (auto &[id, activity] : diagram().sequences()) { for (auto &m : activity.messages()) { if (local_ast_id_map_.find(m.to()) != local_ast_id_map_.end()) { diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.h b/src/sequence_diagram/visitor/translation_unit_visitor.h index beb52f65..ed314b7b 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.h +++ b/src/sequence_diagram/visitor/translation_unit_visitor.h @@ -252,7 +252,7 @@ public: int64_t local_id) const; /** - * @brief Finalize diagram model + * @brief Finalize diagram model for this translation unit */ void finalize(); diff --git a/uml/sequence/class_diagram_generator_sequence.yml b/uml/sequence/class_diagram_generator_sequence.yml index ffad3a03..6bb3c7a7 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 &)" \ No newline at end of file + - function: "clanguml::class_diagram::generators::plantuml::generator::generate(std::ostream &) const" \ No newline at end of file diff --git a/uml/sequence/cli_handle_options_sequence.yml b/uml/sequence/cli_handle_options_sequence.yml index 8d146175..2aea8c7e 100644 --- a/uml/sequence/cli_handle_options_sequence.yml +++ b/uml/sequence/cli_handle_options_sequence.yml @@ -1,6 +1,7 @@ type: sequence combine_free_functions_into_file_participants: true generate_method_arguments: none +debug_mode: true glob: - src/cli/cli_handler.cc - src/config/config.cc @@ -14,6 +15,8 @@ exclude: - r: "clanguml::config::option.*" paths: - src/util/util.h + callee_types: + - operator using_namespace: - clanguml start_from: diff --git a/uml/sequence/diagram_generate_generic_sequence.yml b/uml/sequence/diagram_generate_generic_sequence.yml index c6950c7c..257c97b7 100644 --- a/uml/sequence/diagram_generate_generic_sequence.yml +++ b/uml/sequence/diagram_generate_generic_sequence.yml @@ -7,12 +7,8 @@ using_namespace: - clanguml include: namespaces: - - clang - clanguml::common::generators exclude: - namespaces: - - clanguml::model::tvl - - clanguml::decorators paths: - src/common/model/source_location.h start_from: