diff --git a/docs/test_cases.md b/docs/test_cases.md index 03d7bf9b..29991327 100644 --- a/docs/test_cases.md +++ b/docs/test_cases.md @@ -1,4 +1,15 @@ # Test cases index + + + +* [Class diagrams](#class-diagrams) +* [Sequence diagrams](#sequence-diagrams) +* [Package diagrams](#package-diagrams) +* [Include diagrams](#include-diagrams) +* [Other diagrams](#other-diagrams) + + + ## Class diagrams * [t00002](./test_cases/t00002.md) - Basic class inheritance * [t00003](./test_cases/t00003.md) - Class fields and methods @@ -112,5 +123,5 @@ * [t40001](./test_cases/t40001.md) - Basic include graph diagram test case * [t40002](./test_cases/t40002.md) - Cyclic include graph diagram test case * [t40003](./test_cases/t40003.md) - Dependants and dependencies include diagram filter test -## Configuration diagrams +## Other diagrams * [t90000](./test_cases/t90000.md) - Basic config test diff --git a/src/sequence_diagram/generators/json/sequence_diagram_generator.cc b/src/sequence_diagram/generators/json/sequence_diagram_generator.cc index 41bd3467..c44967d3 100644 --- a/src/sequence_diagram/generators/json/sequence_diagram_generator.cc +++ b/src/sequence_diagram/generators/json/sequence_diagram_generator.cc @@ -242,6 +242,13 @@ void generator::generate_activity(const activity &a, } } +nlohmann::json &generator::current_block_statement() const +{ + assert(!block_statements_stack_.empty()); + + return block_statements_stack_.back().get(); +} + void generator::process_call_message(const model::message &m, std::vector &visited) const { diff --git a/src/sequence_diagram/generators/json/sequence_diagram_generator.h b/src/sequence_diagram/generators/json/sequence_diagram_generator.h index d30a2f48..dfbdbe3f 100644 --- a/src/sequence_diagram/generators/json/sequence_diagram_generator.h +++ b/src/sequence_diagram/generators/json/sequence_diagram_generator.h @@ -40,54 +40,199 @@ using diagram_model = clanguml::sequence_diagram::model::diagram; template using common_generator = clanguml::common::generators::json::generator; +/** + * @brief Sequence diagram JSON generator + */ class generator : public common_generator { public: generator(diagram_config &config, diagram_model &model); + /** + * @brief Main generator method. + * + * This method is called first and coordinates the entire diagram + * generation. + * + * @param ostr Output stream. + */ + void generate(std::ostream &ostr) const override; + + /** + * @brief Generate sequence diagram message. + * + * @param m Message model + * @param parent JSON node + */ void generate_call(const sequence_diagram::model::message &m, nlohmann::json &parent) const; + /** + * @brief Generate sequence diagram participant + * + * @param parent JSON node + * @param id Participant id + * @param force If true, generate the participant even if its not in + * the set of active participants + * @return Id of the generated participant + */ common::id_t generate_participant( nlohmann::json &parent, common::id_t id, bool force = false) const; + /** + * @brief Generate sequence diagram participant by name + * + * This is convienience wrapper over `generate_participant()` by id. + * + * @param parent JSON node + * @param name Full participant name + */ void generate_participant( nlohmann::json &parent, const std::string &name) const; + /** + * @brief Generate sequence diagram activity. + * + * @param a Activity model + * @param visited List of already visited participants, this is necessary + * for breaking infinite recursion on recursive calls + */ void generate_activity(const sequence_diagram::model::activity &a, std::vector &visited) const; - void generate(std::ostream &ostr) const override; - - nlohmann::json ¤t_block_statement() const - { - assert(!block_statements_stack_.empty()); - - return block_statements_stack_.back().get(); - } + /** + * @brief Get reference to the current block statement. + * + * This method returns a reference to the last block statement (e.g if + * statement or for loop) in the call stack. + * + * @return Reference to the current block statement. + */ + nlohmann::json ¤t_block_statement() const; private: + /** + * @brief Check if specified participant has already been generated. + * + * @param id Participant id. + * @return True, if participant has already been generated. + */ bool is_participant_generated(common::id_t id) const; + + /** + * @brief Process call message + * + * @param m Message model + * @param visited List of already visited participants + */ void process_call_message(const model::message &m, std::vector &visited) const; + + /** + * @brief Process `if` statement message + * + * @param m Message model + */ void process_if_message(const model::message &m) const; + + /** + * @brief Process `else if` statement message + */ void process_else_if_message() const; + + /** + * @brief Process `end if` statement message + */ void process_end_if_message() const; - void process_end_conditional_message() const; - void process_conditional_else_message() const; + + /** + * @brief Process `:?` statement message + * + * @param m Message model + */ void process_conditional_message(const model::message &m) const; - void process_end_switch_message() const; - void process_case_message(const model::message &m) const; + + /** + * @brief Process end of conditional statement message + */ + void process_end_conditional_message() const; + + /** + * @brief Process conditional else statement message + */ + void process_conditional_else_message() const; + + /** + * @brief Process `switch` statement message + * + * @param m Message model + */ void process_switch_message(const model::message &m) const; - void process_end_try_message() const; - void process_catch_message() const; + + /** + * @brief Process switch end statement message + */ + void process_end_switch_message() const; + + /** + * @brief Process `switch` `case` statement message + * + * @param m Message model + */ + void process_case_message(const model::message &m) const; + + /** + * @brief Process `try` statement message + * + * @param m Message model + */ void process_try_message(const model::message &m) const; - void process_end_do_message() const; + + /** + * @brief Process `try` end statement message + */ + void process_end_try_message() const; + + /** + * @brief Process `catch` statement message + */ + void process_catch_message() const; + + /** + * @brief Process `do` loop statement message + * + * @param m Message model + */ void process_do_message(const model::message &m) const; - void process_end_for_message() const; + + /** + * @brief Process `do` end statement message + */ + void process_end_do_message() const; + + /** + * @brief Process `for` loop statement message + * + * @param m Message model + */ void process_for_message(const model::message &m) const; - void process_end_while_message() const; + + /** + * @brief Process `for` end statement message + */ + void process_end_for_message() const; + + /** + * @brief Process `while` loop message + * + * @param m Message model + */ void process_while_message(const model::message &m) const; + /** + * @brief Process `while` end loop message + */ + void process_end_while_message() const; + mutable std::set generated_participants_; mutable nlohmann::json json_; diff --git a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.h b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.h index 72185859..fa8b8dec 100644 --- a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.h +++ b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.h @@ -42,35 +42,102 @@ template using common_generator = clanguml::common::generators::plantuml::generator; +/** + * @brief Sequence diagram PlantUML generator + */ class generator : public common_generator { public: generator(diagram_config &config, diagram_model &model); + /** + * @brief Main generator method. + * + * This method is called first and coordinates the entire diagram + * generation. + * + * @param ostr Output stream. + */ + void generate(std::ostream &ostr) const override; + + /** + * @brief Generate sequence diagram message. + * + * @param m Message model + * @param ostr Output stream + */ void generate_call(const clanguml::sequence_diagram::model::message &m, std::ostream &ostr) const; + /** + * @brief Generate sequence diagram return message + * + * @param m Message model + * @param ostr Output stream + */ void generate_return(const clanguml::sequence_diagram::model::message &m, std::ostream &ostr) const; + /** + * @brief Generate sequence diagram participant + * + * @param ostr Output stream + * @param id Participant id + * @param force If true, generate the participant even if its not in + * the set of active participants + * @return Id of the generated participant + */ void generate_participant( std::ostream &ostr, common::id_t id, bool force = false) const; + /** + * @brief Generate sequence diagram participant by name + * + * This is convienience wrapper over `generate_participant()` by id. + * + * @param ostr Output stream + * @param name Full participant name + */ void generate_participant( std::ostream &ostr, const std::string &name) const; + /** + * @brief Generate sequence diagram activity. + * + * @param a Activity model + * @param ostr Output stream + * @param visited List of already visited participants, this is necessary + * for breaking infinite recursion on recursive calls + */ void generate_activity(const clanguml::sequence_diagram::model::activity &a, std::ostream &ostr, std::vector &visited) const; - void generate(std::ostream &ostr) const override; - private: + /** + * @brief Check if specified participant has already been generated. + * + * @param id Participant id. + * @return True, if participant has already been generated. + */ bool is_participant_generated(common::id_t id) const; + /** + * @brief Generate PlantUML alias for participant + * + * @param participant Sequence diagram participant model + * @return Particpant alias + */ + std::string generate_alias(const model::participant &participant) const; + + /** + * @brief Escape the symbols in the name for PlantUML + * + * @param name Full participant name + * @return Escaped name + */ std::string render_name(std::string name) const; mutable std::set generated_participants_; - std::string generate_alias(const model::participant &participant) const; }; } // namespace plantuml diff --git a/src/sequence_diagram/model/activity.cc b/src/sequence_diagram/model/activity.cc index e8f9100e..31313e84 100644 --- a/src/sequence_diagram/model/activity.cc +++ b/src/sequence_diagram/model/activity.cc @@ -31,8 +31,6 @@ std::vector &activity::messages() { return messages_; } const std::vector &activity::messages() const { return messages_; } -void activity::set_from(common::model::diagram_element::id_t f) { from_ = f; } - common::model::diagram_element::id_t activity::from() const { return from_; } } // namespace clanguml::sequence_diagram::model diff --git a/src/sequence_diagram/model/activity.h b/src/sequence_diagram/model/activity.h index 0089c382..404b09b4 100644 --- a/src/sequence_diagram/model/activity.h +++ b/src/sequence_diagram/model/activity.h @@ -25,18 +25,44 @@ namespace clanguml::sequence_diagram::model { +/** + * @brief Model of a sequence diagram activity + */ class activity { public: + /** + * @brief Constructor + * + * @param id Id of the participant parent for the activity + */ activity(common::model::diagram_element::id_t id); + /** + * @brief Add a message call to the activity + * + * @param m Message model + */ void add_message(message m); + /** + * @brief Get list of messages in the activity + * + * @return Reference to list of messages in the activity + */ std::vector &messages(); + /** + * @brief Get list of messages in the activity + * + * @return Reference to list of messages in the activity + */ const std::vector &messages() const; - void set_from(common::model::diagram_element::id_t f); - + /** + * @brief Get the id of activity parent participant + * + * @return Id of activity participant + */ common::model::diagram_element::id_t from() const; private: diff --git a/src/sequence_diagram/model/diagram.cc b/src/sequence_diagram/model/diagram.cc index 2cae6447..e3fde21f 100644 --- a/src/sequence_diagram/model/diagram.cc +++ b/src/sequence_diagram/model/diagram.cc @@ -142,10 +142,6 @@ void diagram::add_case_stmt_message(model::message &&m) } } -bool diagram::started() const { return started_; } - -void diagram::started(bool s) { started_ = s; } - std::map &diagram::sequences() { return sequences_; @@ -175,12 +171,6 @@ std::set &diagram::active_participants() return active_participants_; } -const std::set & -diagram::active_participants() const -{ - return active_participants_; -} - void diagram::print() const { LOG_TRACE(" --- Participants ---"); diff --git a/src/sequence_diagram/model/diagram.h b/src/sequence_diagram/model/diagram.h index 29ca5208..f8d6b7e8 100644 --- a/src/sequence_diagram/model/diagram.h +++ b/src/sequence_diagram/model/diagram.h @@ -27,6 +27,11 @@ namespace clanguml::sequence_diagram::model { +/** + * @brief Model of a sequence diagram + * + * @embed{sequence_model_class.svg} + */ class diagram : public clanguml::common::model::diagram { public: diagram() = default; @@ -36,20 +41,37 @@ public: diagram &operator=(const diagram &) = delete; diagram &operator=(diagram &&) = default; + /** + * @brief Get the diagram model type - in this case sequence. + * + * @return Type of sequence diagram. + */ common::model::diagram_t type() const override; + /** + * @brief Search for element in the diagram by fully qualified name. + * + * @param full_name Fully qualified element name. + * @return Optional reference to a diagram element. + */ common::optional_ref get( const std::string &full_name) const override; + /** + * @brief Search for element in the diagram by id. + * + * @param id Element id. + * @return Optional reference to a diagram element. + */ common::optional_ref get( common::model::diagram_element::id_t id) const override; - std::string to_alias(const std::string &full_name) const; - - inja::json context() const override; - - void print() const; - + /** + * @brief Get participant by id + * + * @param id Participant id. + * @return Optional reference to a diagram element. + */ template common::optional_ref get_participant( common::model::diagram_element::id_t id) @@ -62,52 +84,123 @@ public: static_cast(participants_.at(id).get())); } - template - common::optional_ref get_participant( - common::model::diagram_element::id_t id) const - { - if (participants_.find(id) == participants_.end()) { - return {}; - } - - return common::optional_ref( - static_cast(participants_.at(id).get())); - } - + /** + * @brief Add sequence diagram participant + * + * @param p Sequence diagram participant model + */ void add_participant(std::unique_ptr p); + /** + * @brief Set participant with `id` as active + * + * @param id Id of participant to activate + */ void add_active_participant(common::model::diagram_element::id_t id); + /** + * @brief Get reference to current activity of a participant + * + * @param id Participant id + * @return + */ activity &get_activity(common::model::diagram_element::id_t id); + /** + * @brief Add message to current activity + * + * @param message Message model + */ void add_message(model::message &&message); + /** + * @brief Add block message to the current activity + * + * Block messages represent sequence diagram blocks such as `alt` + * or `loop`. + * + * The block messages can be stacked. + * + * @param message Message model + */ void add_block_message(model::message &&message); + /** + * @brief End current block message + * + * @param message Message model + * @param start_type Type of block statement. + */ void end_block_message( model::message &&message, common::model::message_t start_type); + /** + * @brief Add `switch` block `case` statement + * @param m Message model + */ void add_case_stmt_message(model::message &&m); - bool started() const; - void started(bool s); - + /** + * @brief Get all sequences in the diagram + * + * @return Map of sequences in the diagram + */ std::map &sequences(); + /** + * @brief Get all sequences in the diagram + * + * @return Map of sequences in the diagram + */ const std::map & sequences() const; + /** + * @brief Get map of all participants in the diagram + * + * @return Map of participants in the diagram + */ std::map> &participants(); + /** + * @brief Get map of all participants in the diagram + * + * @return Map of participants in the diagram + */ const std::map> & participants() const; + /** + * @brief Get all active participants in the diagram + * + * @return Set of all active participant ids + */ std::set &active_participants(); - const std::set & - active_participants() const; + /** + * @brief Convert element full name to PlantUML alias. + * + * @todo This method does not belong here - refactor to PlantUML specific + * code. + * + * @param full_name Full name of the diagram element. + * @return PlantUML alias. + */ + std::string to_alias(const std::string &full_name) const; + + /** + * @brief Return the elements JSON context for inja templates. + * + * @return JSON node with elements context. + */ + inja::json context() const override; + + /** + * @brief Debug method for printing entire diagram to console. + */ + void print() const; private: /** diff --git a/src/sequence_diagram/model/message.h b/src/sequence_diagram/model/message.h index b92f87a6..2de4cbc9 100644 --- a/src/sequence_diagram/model/message.h +++ b/src/sequence_diagram/model/message.h @@ -25,29 +25,107 @@ namespace clanguml::sequence_diagram::model { +/** + * @brief Model of a sequence diagram message. + */ class message : public common::model::diagram_element { public: message() = default; + /** + * @brief Constructor + * + * @param type Message type + * @param from Id of originating sequence + */ message(common::model::message_t type, common::model::diagram_element::id_t from); + /** + * @brief Set message type + * + * @param t Message type + */ void set_type(common::model::message_t t); + + /** + * @brief Get message type + * + * @return Message type + */ common::model::message_t type() const; + /** + * @brief Set the id of message source participant + * + * @param f Id of the participant from which message originates + */ void set_from(common::model::diagram_element::id_t f); + + /** + * @brief Get the id of source of message + * + * @return + */ common::model::diagram_element::id_t from() const; + /** + * @brief Set the id of the message target + * + * @param t Id of the message target + */ void set_to(common::model::diagram_element::id_t t); + + /** + * @brief Get the id of the message target + * + * @return Id of the message target + */ common::model::diagram_element::id_t to() const; + /** + * @brief Set the message label + * + * @param name Message label + */ void set_message_name(std::string name); + + /** + * @brief Get the message label + * + * @return Message label + */ const std::string &message_name() const; + /** + * @brief Set the return message type label + * + * @param t Message return type label + */ void set_return_type(std::string t); + + /** + * @brief Get the return message type label + * + * @return Message return type label + */ const std::string &return_type() const; + /** + * @brief Set message scope + * + * Message scope currently means whether the message was called from + * regular statement, or a statement embedded in a statement block condition + * + * @param scope Message scope + */ void set_message_scope(common::model::message_scope_t scope); + + /** + * @brief Get message scope + * + * @return Message scope + */ common::model::message_scope_t message_scope() const; private: diff --git a/src/sequence_diagram/model/participant.h b/src/sequence_diagram/model/participant.h index 65808afc..0a4c4ff7 100644 --- a/src/sequence_diagram/model/participant.h +++ b/src/sequence_diagram/model/participant.h @@ -28,8 +28,16 @@ namespace clanguml::sequence_diagram::model { using clanguml::common::model::template_trait; +/** + * @brief Base class for various types of sequence diagram participants + */ struct participant : public common::model::element, public common::model::stylable_element { + using common::model::element::element; + + /** + * @brief Enum representing stereotype of a participant + */ enum class stereotype_t { participant = 0, actor, @@ -41,20 +49,31 @@ struct participant : public common::model::element, queue }; - using common::model::element::element; - participant(const participant &) = delete; participant(participant &&) noexcept = delete; participant &operator=(const participant &) = delete; participant &operator=(participant &&) = delete; + /** + * Get the type name of the diagram element. + * + * @return Type name of the diagram element. + */ std::string type_name() const override { return "participant"; } + /** + * @brief Create a string representation of the participant + * + * @return Participant representation as string + */ virtual std::string to_string() const; stereotype_t stereotype_{stereotype_t::participant}; }; +/** + * @brief Sequence diagram participant representing a class. + */ struct class_ : public participant, public template_trait { public: class_(const common::model::namespace_ &using_namespace); @@ -64,31 +83,104 @@ public: class_ &operator=(const class_ &) = delete; class_ &operator=(class_ &&) = delete; + /** + * Get the type name of the diagram element. + * + * @return Type name of the diagram element. + */ std::string type_name() const override { return "class"; } + /** + * @brief Check if class is a struct. + * + * @return True, if the class is declared as struct. + */ bool is_struct() const; + + /** + * @brief Set whether the class is a struct. + * + * @param is_struct True, if the class is declared as struct + */ void is_struct(bool is_struct); + /** + * @brief Check if class is a template. + * + * @return True, if the class is a template. + */ bool is_template() const; + + /** + * @brief Set whether the class is a template instantiation. + * + * @param is_template True, if the class is a template + */ void is_template(bool is_template); + /** + * @brief Check if class is a template instantiation. + * + * @return True, if the class is a template instantiation. + */ bool is_template_instantiation() const; + + /** + * @brief Set whether the class is a template instantiation. + * + * @param is_template_instantiation True, if the class is a template + * instantiation. + */ void is_template_instantiation(bool is_template_instantiation); friend bool operator==(const class_ &l, const class_ &r); + /** + * Return elements full name. + * + * @return Fully qualified elements name. + */ std::string full_name(bool relative = true) const override; + /** + * Return elements full name but without namespace. + * + * @return Elements full name without namespace. + */ std::string full_name_no_ns() const override; + /** + * @brief Check if class is a abstract. + * + * @return True, if the class is abstract. + */ bool is_abstract() const; + /** + * @brief Check if class is a typedef/using alias. + * + * @return True, if the class is a typedef/using alias. + */ bool is_alias() const; + /** + * @brief Set whether the class is an alias + * + * @param alias True if the class is a typedef/using alias. + */ void is_alias(bool alias); + /** + * @brief Check if the class is lambda + * @return + */ bool is_lambda() const; + /** + * @brief Set whether the class is a lambda. + * + * @param is_lambda True, if the class is a lambda + */ void is_lambda(bool is_lambda); private: @@ -101,12 +193,23 @@ private: std::string full_name_; }; +/** + * @brief Participant representing a C++ lambda. + */ struct lambda : public class_ { using class_::class_; + /** + * Get the type name of the diagram element. + * + * @return Type name of the diagram element. + */ std::string type_name() const override { return "lambda"; } }; +/** + * @brief Participant mode representing a free function. + */ struct function : public participant { enum class message_render_mode { full, abbreviated, no_arguments }; @@ -117,28 +220,92 @@ struct function : public participant { function &operator=(const function &) = delete; function &operator=(function &&) = delete; + /** + * Get the type name of the diagram element. + * + * @return Type name of the diagram element. + */ std::string type_name() const override { return "function"; } + /** + * Return elements full name. + * + * @return Fully qualified elements name. + */ std::string full_name(bool relative = true) const override; + /** + * Return elements full name but without namespace. + * + * @return Elements full name without namespace. + */ std::string full_name_no_ns() const override; + /** + * @brief Render function name as message label + * + * @param mode Function argument render mode + * @return Message label + */ virtual std::string message_name(message_render_mode mode) const; + /** + * @brief Check if function is const + * + * @return True, if function is const + */ bool is_const() const; + /** + * @brief Set whether the function is const + * + * @param c True, if function is const + */ void is_const(bool c); + /** + * @brief Check, if the function has no return value + * + * @return True, if the function has no return value + */ bool is_void() const; + /** + * @brief Set whether the function has a return value + * + * @param v True, if the function has no return value + */ void is_void(bool v); + /** + * @brief Check, if the function is static + * + * @return True, if the function is static + */ bool is_static() const; + /** + * @brief Set whether the function is static + * + * @param v True, if the function is static + */ void is_static(bool s); + /** + * @brief Add a function parameter + * + * @note In sequence diagrams we don't care about relationships from + * function or method parameters, so we don't need to model them in detail. + * + * @param a Function parameter label including name and type + */ void add_parameter(const std::string &a); + /** + * @brief Get the list of function parameters + * + * @return List of function parameters + */ const std::vector ¶meters() const; private: @@ -148,6 +315,9 @@ private: std::vector parameters_; }; +/** + * @brief Participant model representing a method + */ struct method : public function { method(const common::model::namespace_ &using_namespace); @@ -156,26 +326,79 @@ struct method : public function { method &operator=(const method &) = delete; method &operator=(method &&) = delete; + /** + * Get the type name of the diagram element. + * + * @return Type name of the diagram element. + */ std::string type_name() const override { return "method"; } + /** + * @brief Get method name + * @return Method name + */ std::string method_name() const; - std::string alias() const override; - + /** + * @brief Set method name + * + * @param name Method name + */ void set_method_name(const std::string &name); + /** + * @brief Get the participant PlantUML alias + * + * @todo This method does not belong here - refactor to PlantUML specific + * code. + * + * @return PlantUML alias for the participant to which this method belongs + * to. + */ + std::string alias() const override; + + /** + * @brief Set the id of the participant to which this method belongs to. + * + * @param id Id of the class to which this method belongs to + */ void set_class_id(diagram_element::id_t id); + /** + * @brief Set full qualified name of the class + * + * @param name Name of the class including namespace + */ void set_class_full_name(const std::string &name); + /** + * @brief Get the class full name. + * + * @return Class full name + */ const auto &class_full_name() const; + /** + * Return elements full name. + * + * @return Fully qualified elements name. + */ std::string full_name(bool /*relative*/) const override; std::string message_name(message_render_mode mode) const override; + /** + * @brief Get the class id + * + * @return Class id + */ diagram_element::id_t class_id() const; + /** + * @brief Create a string representation of the participant + * + * @return Participant representation as string + */ std::string to_string() const override; private: @@ -184,6 +407,9 @@ private: std::string class_full_name_; }; +/** + * @brief Participant model representing a function template. + */ struct function_template : public function, public template_trait { function_template(const common::model::namespace_ &using_namespace); @@ -192,12 +418,33 @@ struct function_template : public function, public template_trait { function_template &operator=(const function_template &) = delete; function_template &operator=(function_template &&) = delete; + /** + * Get the type name of the diagram element. + * + * @return Type name of the diagram element. + */ std::string type_name() const override { return "function_template"; } + /** + * Return elements full name. + * + * @return Fully qualified elements name. + */ std::string full_name(bool relative = true) const override; + /** + * Return elements full name but without namespace. + * + * @return Elements full name without namespace. + */ std::string full_name_no_ns() const override; + /** + * @brief Render function name as message label + * + * @param mode Function argument render mode + * @return Message label + */ std::string message_name(message_render_mode mode) const override; }; } // namespace clanguml::sequence_diagram::model diff --git a/src/sequence_diagram/visitor/call_expression_context.h b/src/sequence_diagram/visitor/call_expression_context.h index d400bb66..b65a23e2 100644 --- a/src/sequence_diagram/visitor/call_expression_context.h +++ b/src/sequence_diagram/visitor/call_expression_context.h @@ -28,70 +28,268 @@ namespace clanguml::sequence_diagram::visitor { +/** + * @brief This class is used to track current context of the call expressions. + * + * When traversing AST for call expressions, we need to keep the state of + * the current context, for instance whether we are in a `for` loop or + * an `if` block, as well as the current parent of the call expression + * e.g. a class method or function. + */ struct call_expression_context { call_expression_context(); + /** + * @brief Reset call expression context to the original state. + */ void reset(); - void dump(); - + /** + * @brief Verify that the context is in a valid state. + * + * Context can only be in a single state (for instance inside a function). + * + * @return True, if the context is in a valid state. + */ bool valid() const; + /** + * @brief + * + * @return Current AST context + */ clang::ASTContext *get_ast_context() const; + /** + * @brief Set the current context to a class. + * + * @param cls Class declaration. + */ void update(clang::CXXRecordDecl *cls); + /** + * @brief Set the current context to a class template specialization. + * + * @param clst Class template specialization declaration. + */ void update(clang::ClassTemplateSpecializationDecl *clst); + /** + * @brief Set the current context to a class template. + * + * @param clst Class template declaration. + */ void update(clang::ClassTemplateDecl *clst); + /** + * @brief Set the current context to a class method. + * + * @param method Class method declaration. + */ void update(clang::CXXMethodDecl *method); + /** + * @brief Set the current context to a function. + * + * @param function Function declaration. + */ void update(clang::FunctionDecl *function); + /** + * @brief Set the current context to a function template. + * + * @param function_template Function template declaration. + */ void update(clang::FunctionTemplateDecl *function_template); - std::int64_t caller_id() const; - - std::int64_t lambda_caller_id() const; - + /** + * @brief Set current caller to id of the current participant. + * + * @param id Set current caller id. + */ void set_caller_id(std::int64_t id); + /** + * @brief Get current caller id + * + * @return Id of the current caller participant + */ + std::int64_t caller_id() const; + + /** + * @brief Get the id of the current lambda caller. + * + * Since lambdas can be nested within methods and functions, they have + * a separate caller id field. + * + * @return Current lambda caller id, or 0 if current caller is not lambda. + */ + std::int64_t lambda_caller_id() const; + + /** + * @brief Enter a lambda expression + * + * @param id Lambda id + */ void enter_lambda_expression(std::int64_t id); + /** + * @brief Leave current lambda expression + */ void leave_lambda_expression(); + /** + * @brief Get current `if` statement block + * + * @return `if` statement block. + */ clang::IfStmt *current_ifstmt() const; + /** + * @brief Enter `if` statement block + * + * @param stmt `if` statement block + */ void enter_ifstmt(clang::IfStmt *stmt); + + /** + * @brief Leave `if` statement block + */ void leave_ifstmt(); + /** + * @brief Enter `else if` statement block + * + * @param stmt `if` statement block + */ void enter_elseifstmt(clang::IfStmt *stmt); + + /** + * @brief Get current `else if` statement block + * + * @return `if` statement block. + */ clang::IfStmt *current_elseifstmt() const; + /** + * @brief Get current loop statement block + * + * @return Loop statement block. + */ clang::Stmt *current_loopstmt() const; + + /** + * @brief Enter loop statement block + * + * @param stmt Loop statement block + */ void enter_loopstmt(clang::Stmt *stmt); + + /** + * @brief Leave loop statement block + */ void leave_loopstmt(); + /** + * @brief Get current `try` statement block + * + * @return `try` statement block. + */ clang::Stmt *current_trystmt() const; + + /** + * @brief Enter `try` statement block + * + * @param stmt `try` statement block + */ void enter_trystmt(clang::Stmt *stmt); + + /** + * @brief Leave `try` statement block + */ void leave_trystmt(); + /** + * @brief Get current `switch` statement block + * + * @return `switch` statement block. + */ clang::SwitchStmt *current_switchstmt() const; + + /** + * @brief Enter `switch` statement block + * + * @param stmt `switch` statement block + */ void enter_switchstmt(clang::SwitchStmt *stmt); + + /** + * @brief Leave `switch` statement block + */ void leave_switchstmt(); + /** + * @brief Get current `:?` statement block + * + * @return `:?` statement block. + */ clang::ConditionalOperator *current_conditionaloperator() const; + + /** + * @brief Enter `:?` statement block + * + * @param stmt `:?` statement block + */ void enter_conditionaloperator(clang::ConditionalOperator *stmt); + + /** + * @brief Leave `:?` statement block + */ void leave_conditionaloperator(); + /** + * @brief Get current call expression + * + * @return Call expression + */ clang::CallExpr *current_callexpr() const; + + /** + * @brief Enter a call expression + * + * @param stmt Call expression + */ void enter_callexpr(clang::CallExpr *expr); + + /** + * @brief Leave call expression + */ void leave_callexpr(); + /** + * @brief Check, if a statement is contained in a control statement + * + * This method is used to check if `stmt` is contained in control + * statement of a block, for instance: + * + * ```cpp + * if(a.method1()) {} + * ``` + * it will return `true` for `stmt` representing `method1()` call + * expression. + * + * @param stmt Statement + * @return True, if `stmt` is contained in control expression of a + * statement block + */ bool is_expr_in_current_control_statement_condition( const clang::Stmt *stmt) const; + /** + * @brief Print the current call expression stack for debugging. + */ + void dump(); + clang::CXXRecordDecl *current_class_decl_{nullptr}; clang::ClassTemplateDecl *current_class_template_decl_{nullptr}; clang::ClassTemplateSpecializationDecl diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.h b/src/sequence_diagram/visitor/translation_unit_visitor.h index 49ca52c5..0e7ae533 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.h +++ b/src/sequence_diagram/visitor/translation_unit_visitor.h @@ -35,16 +35,33 @@ using common::model::template_parameter; std::string to_string(const clang::FunctionTemplateDecl *decl); +/** + * @brief Sequence diagram translation unit visitor + * + * This class implements the `clang::RecursiveASTVisitor` interface + * for selected visitors relevant to generating sequence diagrams. + */ class translation_unit_visitor : public clang::RecursiveASTVisitor, public common::visitor::translation_unit_visitor { public: + /** + * @brief Constructor. + * + * @param sm Current source manager reference + * @param diagram Diagram model + * @param config Diagram configuration + */ translation_unit_visitor(clang::SourceManager &sm, clanguml::sequence_diagram::model::diagram &diagram, const clanguml::config::sequence_diagram &config); ~translation_unit_visitor() override = default; + /** + * \defgroup Implementation of ResursiveASTVisitor methods + * @{ + */ bool shouldVisitTemplateInstantiations(); bool VisitCallExpr(clang::CallExpr *expr); @@ -99,19 +116,50 @@ public: bool TraverseDefaultStmt(clang::DefaultStmt *stmt); bool TraverseConditionalOperator(clang::ConditionalOperator *stmt); + /** @} */ + /** + * @brief Get diagram model reference + * + * @return Reference to diagram model created by the visitor + */ clanguml::sequence_diagram::model::diagram &diagram(); + /** + * @brief Get diagram model reference + * + * @return Reference to diagram model created by the visitor + */ const clanguml::sequence_diagram::model::diagram &diagram() const; + /** + * @brief Get diagram config instance + * + * @return Reference to config instance + */ const clanguml::config::sequence_diagram &config() const; + /** + * @brief Get current call expression context reference + * + * @return Reference to the current call expression context + */ call_expression_context &context(); + /** + * @brief Get current call expression context reference + * + * @return Reference to the current call expression context + */ const call_expression_context &context() const; - void finalize(); - + /** + * @brief Get participant by declaration + * + * @tparam T Participant type + * @param decl Clang entity declaration + * @return Optional reference to participant diagram element + */ template common::optional_ref get_participant(const clang::Decl *decl) { @@ -124,6 +172,13 @@ public: return get_participant(unique_participant_id.value()); } + /** + * @brief Get participant by declaration + * + * @tparam T Participant type + * @param decl Clang entity declaration + * @return Optional reference to participant diagram element + */ template common::optional_ref get_participant(const clang::Decl *decl) const { @@ -136,6 +191,13 @@ public: return get_participant(unique_participant_id.value()); } + /** + * @brief Get participant by global element id + * + * @tparam T Participant type + * @param id Global element id + * @return Optional reference to participant diagram element + */ template common::optional_ref get_participant( const common::model::diagram_element::id_t id) @@ -147,6 +209,13 @@ public: *(static_cast(diagram().participants().at(id).get()))); } + /** + * @brief Get participant by global element id + * + * @tparam T Participant type + * @param id Global element id + * @return Optional reference to participant diagram element + */ template common::optional_ref get_participant( common::model::diagram_element::id_t id) const @@ -158,24 +227,94 @@ public: *(static_cast(diagram().participants().at(id).get()))); } - /// Store the mapping from local clang entity id (obtained using - /// getID()) method to clang-uml global id + /** + * @brief Store the mapping from local clang entity id (obtained using + * getID()) method to clang-uml global id + * + * @todo Refactor to @ref ast_id_mapper + * + * @param local_id Local AST element id + * @param global_id Globa diagram element id + */ void set_unique_id( int64_t local_id, common::model::diagram_element::id_t global_id); - /// Retrieve the global clang-uml entity id based on the clang local id + /** + * @brief Retrieve the global `clang-uml` entity id based on the Clang + * local id + * @param local_id AST local element id + * @return Global diagram element id + */ std::optional get_unique_id( int64_t local_id) const; + /** + * @brief Finalize diagram model + */ + void finalize(); + private: + /** + * @brief Check if the diagram should include a declaration. + * + * @param decl Clang declaration. + * @return True, if the entity should be included in the diagram. + */ bool should_include(const clang::TagDecl *decl) const; + + /** + * @brief Check if the diagram should include a lambda expression. + * + * @param expr Lambda expression. + * @return True, if the expression should be included in the diagram. + */ bool should_include(const clang::LambdaExpr *expr) const; + + /** + * @brief Check if the diagram should include a call expression. + * + * @param expr Call expression. + * @return True, if the expression should be included in the diagram. + */ bool should_include(const clang::CallExpr *expr) const; + + /** + * @brief Check if the diagram should include a declaration. + * + * @param decl Clang declaration. + * @return True, if the entity should be included in the diagram. + */ bool should_include(const clang::CXXMethodDecl *decl) const; + + /** + * @brief Check if the diagram should include a declaration. + * + * @param decl Clang declaration. + * @return True, if the entity should be included in the diagram. + */ bool should_include(const clang::FunctionDecl *decl) const; + + /** + * @brief Check if the diagram should include a declaration. + * + * @param decl Clang declaration. + * @return True, if the entity should be included in the diagram. + */ bool should_include(const clang::FunctionTemplateDecl *decl) const; + + /** + * @brief Check if the diagram should include a declaration. + * + * @param decl Clang declaration. + * @return True, if the entity should be included in the diagram. + */ bool should_include(const clang::ClassTemplateDecl *decl) const; + /** + * @todo Refactor this group of methods to @ref template_builder + * + * @{ + */ std::unique_ptr create_class_model(clang::CXXRecordDecl *cls); @@ -251,31 +390,107 @@ private: const std::string &full_name) const; std::string simplify_system_template(const std::string &full_name) const; + /** }@ */ + /** + * @brief Assuming `cls` is a lambda, create it's full name. + * + * @note Currently, lambda names are generated using their source code + * location including file path, line and column to ensure + * unique names. + * + * @param cls Lambda declaration + * @return Full lambda unique name + */ std::string make_lambda_name(const clang::CXXRecordDecl *cls) const; + /** + * @brief Check if template is a smart pointer + * + * @param primary_template Template declaration + * @return True, if template declaration is a smart pointer + */ bool is_smart_pointer(const clang::TemplateDecl *primary_template) const; + /** + * @brief Check, the callee is a template specialization + * + * @param dependent_member_expr Dependent member expression + * @return True, if the callee is a template specialization + */ bool is_callee_valid_template_specialization( const clang::CXXDependentScopeMemberExpr *dependent_member_expr) const; + /** + * @brief Handle a operator call expresion + * + * @param m Message model + * @param operator_call_expr Operator call expression + * @return True, if `m` contains now a valid call expression model + */ bool process_operator_call_expression(model::message &m, const clang::CXXOperatorCallExpr *operator_call_expr); + /** + * @brief Handle a class method call expresion + * + * @param m Message model + * @param method_call_expr Operator call expression + * @return True, if `m` contains now a valid call expression model + */ bool process_class_method_call_expression( - model::message &m, const clang::CXXMemberCallExpr *operator_call_expr); + model::message &m, const clang::CXXMemberCallExpr *method_call_expr); + /** + * @brief Handle a class template method call expresion + * + * @param m Message model + * @param expr Class template method call expression + * @return True, if `m` contains now a valid call expression model + */ bool process_class_template_method_call_expression( model::message &m, const clang::CallExpr *expr); + /** + * @brief Handle a function call expresion + * + * @param m Message model + * @param expr Function call expression + * @return True, if `m` contains now a valid call expression model + */ bool process_function_call_expression( model::message &m, const clang::CallExpr *expr); + /** + * @brief Handle an unresolved lookup call expresion + * + * Unresolved lookup expression is a reference to a name which Clang was + * not able to look up during parsing but could not resolve to a + * specific declaration. + * + * @param m Message model + * @param expr Call expression + * @return True, if `m` contains now a valid call expression model + */ bool process_unresolved_lookup_call_expression( model::message &m, const clang::CallExpr *expr) const; + /** + * @brief Register a message model `m` with a call expression + * + * This is used to know whether a model for a specific call expression + * has already been created, but not yet added to the diagram. + * + * @param expr Call expresion + * @param m Message model + */ void push_message(clang::CallExpr *expr, model::message &&m); + /** + * @brief Move a message model to diagram. + * + * @param expr Call expression + */ void pop_message_to_diagram(clang::CallExpr *expr); // Reference to the output diagram model @@ -286,16 +501,20 @@ private: call_expression_context call_expression_context_; - /// This is used to generate messages in proper order in case of - /// nested call expressions (e.g. a(b(c(), d())), as they need to - /// be added to the diagram sequence after the visitor leaves the - /// call expression AST node + /** + * This is used to generate messages in proper order in case of nested call + * expressions (e.g. a(b(c(), d())), as they need to be added to the diagram + * sequence after the visitor leaves the call expression AST node + */ std::map call_expr_message_map_; std::map> forward_declarations_; + /** + * @todo Refactor to @ref ast_id_mapper + */ std::mapgetID() */ int64_t, /* global ID based on full name */ common::model::diagram_element::id_t> local_ast_id_map_; diff --git a/thirdparty/doxygen-awesome-css/doxygen-awesome.css b/thirdparty/doxygen-awesome-css/doxygen-awesome.css index 8641d6a4..7729fe9d 100644 --- a/thirdparty/doxygen-awesome-css/doxygen-awesome.css +++ b/thirdparty/doxygen-awesome-css/doxygen-awesome.css @@ -2571,4 +2571,9 @@ h2:hover a.anchorlink, h1:hover a.anchorlink, h3:hover a.anchorlink, h4:hover a. /* Preserve newlines in highlight.js code blocks */ div.hljs { white-space: pre; +} + +span.copyright { + display: block; + float: left; } \ No newline at end of file diff --git a/uml/class/sequence_model_class.yml b/uml/class/sequence_model_class.yml index c4772c04..92916509 100644 --- a/uml/class/sequence_model_class.yml +++ b/uml/class/sequence_model_class.yml @@ -1,6 +1,7 @@ type: class -include_relations_also_as_members: false +include_relations_also_as_members: true generate_method_arguments: none +generate_packages: true glob: - src/common/model/*.cc - src/sequence_diagram/model/*.cc @@ -8,11 +9,21 @@ include: namespaces: - clanguml::common::model - clanguml::sequence_diagram::model + context: + - clanguml::sequence_diagram::model::diagram + - clanguml::sequence_diagram::model::message + - clanguml::sequence_diagram::model::activity + subclasses: + - clanguml::sequence_diagram::model::participant exclude: relationships: - dependency + method_types: + - constructor + - destructor + - operator using_namespace: - - clanguml::sequence_diagram::model + - clanguml plantuml: before: - 'title clang-uml sequence diagram model' \ No newline at end of file diff --git a/util/generate_test_cases_docs.py b/util/generate_test_cases_docs.py index bb87efc8..b5c48bfb 100755 --- a/util/generate_test_cases_docs.py +++ b/util/generate_test_cases_docs.py @@ -29,6 +29,12 @@ with open(r'tests/test_cases.yaml') as f: # Generate test_cases.md index with open(r'docs/test_cases.md', 'w') as tc_index: tc_index.write('# Test cases index\n') + tc_index.write("* [Class diagrams](#class-diagrams)") + tc_index.write("* [Sequence diagrams](#sequence-diagrams)") + tc_index.write("* [Package diagrams](#package-diagrams)") + tc_index.write("* [Include diagrams](#include-diagrams)") + tc_index.write("* [Other diagrams](#other-diagrams)") + tc_index.write(" ") for test_group, test_cases in test_groups.items(): tc_index.write(f'## {test_group}\n') for test_case in test_cases: