diff --git a/src/common/clang_utils.cc b/src/common/clang_utils.cc index 884dc3b1..dea23d68 100644 --- a/src/common/clang_utils.cc +++ b/src/common/clang_utils.cc @@ -849,4 +849,30 @@ bool parse_source_location(const std::string &location_str, std::string &file, return true; } +std::optional get_expression_comment( + const clang::SourceManager &sm, const clang::ASTContext &context, + const clang::Stmt *stmt) +{ + // First get the first line of the expression + auto expr_begin = stmt->getSourceRange().getBegin(); + const auto expr_begin_line = sm.getSpellingLineNumber(expr_begin); + + if (!context.Comments.empty()) + for (const auto [offset, raw_comment] : + *context.Comments.getCommentsInFile(sm.getMainFileID())) { + + auto comment = + raw_comment->getFormattedText(sm, sm.getDiagnostics()); + + const auto comment_end_line = sm.getSpellingLineNumber( + raw_comment->getSourceRange().getEnd()); + + if (expr_begin_line == comment_end_line || + expr_begin_line == comment_end_line + 1) + return comment; + } + + return {}; +} + } // namespace clanguml::common diff --git a/src/common/clang_utils.h b/src/common/clang_utils.h index bb84460f..538176c8 100644 --- a/src/common/clang_utils.h +++ b/src/common/clang_utils.h @@ -282,4 +282,8 @@ clang::QualType dereference(clang::QualType type); std::pair> consume_type_context(clang::QualType type); +std::optional get_expression_comment( + const clang::SourceManager &sm, const clang::ASTContext &context, + const clang::Stmt *stmt); + } // namespace clanguml::common diff --git a/src/config/config.h b/src/config/config.h index 9f20dca5..134e9437 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -480,6 +480,7 @@ struct inheritable_diagram_options { option generate_condition_statements{ "generate_condition_statements", false}; option> participants_order{"participants_order"}; + option generate_message_comments{"generate_message_comments", false}; option debug_mode{"debug_mode", false}; option generate_metadata{"generate_metadata", true}; diff --git a/src/config/schema.h b/src/config/schema.h index adedd86c..8bf77a3b 100644 --- a/src/config/schema.h +++ b/src/config/schema.h @@ -201,6 +201,7 @@ types: combine_free_functions_into_file_participants: !optional bool generate_return_types: !optional bool generate_condition_statements: !optional bool + generate_message_comments: !optional bool participants_order: !optional [string] start_from: !optional [source_location_t] # deprecated -> 'from' from: !optional [source_location_t] @@ -311,6 +312,7 @@ root: combine_free_functions_into_file_participants: !optional bool generate_return_types: !optional bool generate_condition_statements: !optional bool + generate_message_comments: !optional bool generate_packages: !optional bool group_methods: !optional bool package_type: !optional package_type_t diff --git a/src/config/yaml_decoders.cc b/src/config/yaml_decoders.cc index e625640b..2c07deb8 100644 --- a/src/config/yaml_decoders.cc +++ b/src/config/yaml_decoders.cc @@ -603,6 +603,7 @@ template <> struct convert { get_option(node, rhs.relative_to); get_option(node, rhs.participants_order); get_option(node, rhs.generate_method_arguments); + get_option(node, rhs.generate_message_comments); // Ensure relative_to has a value if (!rhs.relative_to.has_value) @@ -792,6 +793,7 @@ template <> struct convert { get_option(node, rhs.combine_free_functions_into_file_participants); get_option(node, rhs.generate_return_types); get_option(node, rhs.generate_condition_statements); + get_option(node, rhs.generate_message_comments); rhs.base_directory.set(node["__parent_path"].as()); get_option(node, rhs.relative_to); diff --git a/src/config/yaml_emitters.cc b/src/config/yaml_emitters.cc index 3f415371..8c72f385 100644 --- a/src/config/yaml_emitters.cc +++ b/src/config/yaml_emitters.cc @@ -325,6 +325,7 @@ YAML::Emitter &operator<<( out << c.generate_method_arguments; out << c.generate_return_types; out << c.participants_order; + out << c.generate_message_comments; } else if (const auto *pd = dynamic_cast(&c); pd != nullptr) { diff --git a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc index e0fefc49..ef3ca714 100644 --- a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc +++ b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc @@ -83,6 +83,8 @@ void generator::generate_call(const message &m, std::ostream &ostr) const print_debug(m, ostr); + generate_message_comment(ostr, m); + ostr << from_alias << " " << common::generators::plantuml::to_plantuml(message_t::kCall) << " "; @@ -178,6 +180,7 @@ void generator::generate_activity(const activity &a, std::ostream &ostr, } else if (m.type() == message_t::kIf) { print_debug(m, ostr); + generate_message_comment(ostr, m); ostr << "alt"; if (const auto &text = m.condition_text(); text.has_value()) ostr << " " << text.value(); @@ -199,6 +202,7 @@ void generator::generate_activity(const activity &a, std::ostream &ostr, } else if (m.type() == message_t::kWhile) { print_debug(m, ostr); + generate_message_comment(ostr, m); ostr << "loop"; if (const auto &text = m.condition_text(); text.has_value()) ostr << " " << text.value(); @@ -209,6 +213,7 @@ void generator::generate_activity(const activity &a, std::ostream &ostr, } else if (m.type() == message_t::kFor) { print_debug(m, ostr); + generate_message_comment(ostr, m); ostr << "loop"; if (const auto &text = m.condition_text(); text.has_value()) ostr << " " << text.value(); @@ -219,6 +224,7 @@ void generator::generate_activity(const activity &a, std::ostream &ostr, } else if (m.type() == message_t::kDo) { print_debug(m, ostr); + generate_message_comment(ostr, m); ostr << "loop"; if (const auto &text = m.condition_text(); text.has_value()) ostr << " " << text.value(); @@ -229,6 +235,7 @@ void generator::generate_activity(const activity &a, std::ostream &ostr, } else if (m.type() == message_t::kTry) { print_debug(m, ostr); + generate_message_comment(ostr, m); ostr << "group try\n"; } else if (m.type() == message_t::kCatch) { @@ -241,6 +248,7 @@ void generator::generate_activity(const activity &a, std::ostream &ostr, } else if (m.type() == message_t::kSwitch) { print_debug(m, ostr); + generate_message_comment(ostr, m); ostr << "group switch\n"; } else if (m.type() == message_t::kCase) { @@ -252,6 +260,7 @@ void generator::generate_activity(const activity &a, std::ostream &ostr, } else if (m.type() == message_t::kConditional) { print_debug(m, ostr); + generate_message_comment(ostr, m); ostr << "alt"; if (const auto &text = m.condition_text(); text.has_value()) ostr << " " << text.value(); @@ -267,6 +276,33 @@ void generator::generate_activity(const activity &a, std::ostream &ostr, } } +void generator::generate_message_comment( + std::ostream &ostr, const model::message &m) const +{ + if (!config().generate_message_comments() || !m.comment()) + return; + + const auto &from = model().get_participant(m.from()); + + if (!from) + return; + + if (m.type() == message_t::kCall) { + const auto &to = model().get_participant(m.to()); + if (!to) + return; + + ostr << "note over " << generate_alias(from.value()) << ", " + << generate_alias(to.value()) << '\n'; + } + else { + ostr << "note over " << generate_alias(from.value()) << '\n'; + } + + ostr << m.comment().value() << '\n'; + ostr << "end note" << '\n'; +} + void generator::generate_participant( std::ostream &ostr, const std::string &name) const { diff --git a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.h b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.h index 14d547d2..17dc676f 100644 --- a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.h +++ b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.h @@ -147,6 +147,9 @@ private: model::function::message_render_mode select_method_arguments_render_mode() const; + void generate_message_comment( + std::ostream &ostr, const model::message &m) const; + mutable std::set generated_participants_; mutable std::vector already_generated_in_static_context_; }; diff --git a/src/sequence_diagram/model/message.cc b/src/sequence_diagram/model/message.cc index 69c059bb..54c7235c 100644 --- a/src/sequence_diagram/model/message.cc +++ b/src/sequence_diagram/model/message.cc @@ -58,6 +58,16 @@ void message::set_return_type(std::string t) { return_type_ = std::move(t); } const std::string &message::return_type() const { return return_type_; } +const std::optional &message::comment() const { return comment_; } + +void message::set_comment(std::string c) { comment_ = std::move(c); } + +void message::set_comment(const std::optional &c) +{ + if (c) + set_comment(c.value()); +} + void message::set_message_scope(common::model::message_scope_t scope) { scope_ = scope; diff --git a/src/sequence_diagram/model/message.h b/src/sequence_diagram/model/message.h index 0fb93720..1ca7a145 100644 --- a/src/sequence_diagram/model/message.h +++ b/src/sequence_diagram/model/message.h @@ -119,6 +119,12 @@ public: */ const std::string &return_type() const; + const std::optional &comment() const; + + void set_comment(std::string c); + + void set_comment(const std::optional &c); + /** * @brief Set message scope * @@ -172,6 +178,8 @@ private: std::optional condition_text_; + std::optional comment_; + bool in_static_declaration_context_{false}; }; diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index fe01c704..6ffaffd8 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -613,6 +613,8 @@ bool translation_unit_visitor::TraverseIfStmt(clang::IfStmt *stmt) message m{message_t::kElseIf, current_caller_id}; set_source_location(*stmt, m); m.condition_text(condition_text); + m.set_comment(clanguml::common::get_expression_comment( + source_manager(), *context().get_ast_context(), stmt)); diagram().add_block_message(std::move(m)); } else { @@ -621,6 +623,8 @@ bool translation_unit_visitor::TraverseIfStmt(clang::IfStmt *stmt) message m{message_t::kIf, current_caller_id}; set_source_location(*stmt, m); m.condition_text(condition_text); + m.set_comment(clanguml::common::get_expression_comment( + source_manager(), *context().get_ast_context(), stmt)); diagram().add_block_message(std::move(m)); } } @@ -655,6 +659,8 @@ bool translation_unit_visitor::TraverseWhileStmt(clang::WhileStmt *stmt) message m{message_t::kWhile, current_caller_id}; set_source_location(*stmt, m); m.condition_text(condition_text); + m.set_comment(clanguml::common::get_expression_comment( + source_manager(), *context().get_ast_context(), stmt)); diagram().add_block_message(std::move(m)); } RecursiveASTVisitor::TraverseWhileStmt(stmt); @@ -685,6 +691,8 @@ bool translation_unit_visitor::TraverseDoStmt(clang::DoStmt *stmt) message m{message_t::kDo, current_caller_id}; set_source_location(*stmt, m); m.condition_text(condition_text); + m.set_comment(clanguml::common::get_expression_comment( + source_manager(), *context().get_ast_context(), stmt)); diagram().add_block_message(std::move(m)); } @@ -716,6 +724,10 @@ bool translation_unit_visitor::TraverseForStmt(clang::ForStmt *stmt) message m{message_t::kFor, current_caller_id}; set_source_location(*stmt, m); m.condition_text(condition_text); + + m.set_comment(clanguml::common::get_expression_comment( + source_manager(), *context().get_ast_context(), stmt)); + diagram().add_block_message(std::move(m)); } @@ -742,6 +754,8 @@ bool translation_unit_visitor::TraverseCXXTryStmt(clang::CXXTryStmt *stmt) context().enter_trystmt(stmt); message m{message_t::kTry, current_caller_id}; set_source_location(*stmt, m); + m.set_comment(clanguml::common::get_expression_comment( + source_manager(), *context().get_ast_context(), stmt)); diagram().add_block_message(std::move(m)); } @@ -800,6 +814,8 @@ bool translation_unit_visitor::TraverseCXXForRangeStmt( message m{message_t::kFor, current_caller_id}; set_source_location(*stmt, m); m.condition_text(condition_text); + m.set_comment(clanguml::common::get_expression_comment( + source_manager(), *context().get_ast_context(), stmt)); diagram().add_block_message(std::move(m)); } @@ -825,6 +841,8 @@ bool translation_unit_visitor::TraverseSwitchStmt(clang::SwitchStmt *stmt) context().enter_switchstmt(stmt); model::message m{message_t::kSwitch, current_caller_id}; set_source_location(*stmt, m); + m.set_comment(clanguml::common::get_expression_comment( + source_manager(), *context().get_ast_context(), stmt)); diagram().add_block_message(std::move(m)); } @@ -891,6 +909,8 @@ bool translation_unit_visitor::TraverseConditionalOperator( model::message m{message_t::kConditional, current_caller_id}; set_source_location(*stmt, m); m.condition_text(condition_text); + m.set_comment(clanguml::common::get_expression_comment( + source_manager(), *context().get_ast_context(), stmt)); diagram().add_block_message(std::move(m)); } @@ -1015,6 +1035,10 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr) } if (m.from() > 0 && m.to() > 0) { + auto expr_comment = clanguml::common::get_expression_comment( + source_manager(), *context().get_ast_context(), expr); + m.set_comment(expr_comment); + if (diagram().sequences().find(m.from()) == diagram().sequences().end()) { activity a{m.from()}; diff --git a/tests/t20029/.clang-uml b/tests/t20029/.clang-uml index cc1ccd3a..cc9e5703 100644 --- a/tests/t20029/.clang-uml +++ b/tests/t20029/.clang-uml @@ -1,5 +1,7 @@ compilation_database_dir: .. output_directory: diagrams +add_compile_flags: + - -fparse-all-comments diagrams: t20029_sequence: type: sequence @@ -15,6 +17,7 @@ diagrams: - clanguml::t20029 from: - function: clanguml::t20029::tmain() + generate_message_comments: true participants_order: - clanguml::t20029::tmain() - clanguml::t20029::Encoder> diff --git a/tests/t20029/t20029.cc b/tests/t20029/t20029.cc index 3c89144e..4d1345f4 100644 --- a/tests/t20029/t20029.cc +++ b/tests/t20029/t20029.cc @@ -56,8 +56,10 @@ int tmain() { auto pool = std::make_shared>>(); + // Establish connection to the remote server synchronously pool->connect(); + // Repeat for each line in the input stream for (std::string line; std::getline(std::cin, line);) { if (!pool->send(std::move(line))) break; diff --git a/tests/test_cases.cc b/tests/test_cases.cc index 76c7c529..d3fd3824 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -46,6 +46,11 @@ load_config(const std::string &test_name) LOG_DBG("Loading compilation database from {}", res.first.compilation_database_dir()); + std::vector remove_compile_flags{ + std::string{"-Wno-class-memaccess"}}; + + res.first.remove_compile_flags.set(remove_compile_flags); + res.second = clanguml::common::compilation_database::auto_detect_from_directory( res.first);