Added handling of comment decorators (skip, note) in sequence diagram comments (#194)
This commit is contained in:
@@ -42,7 +42,7 @@ Main features supported so far include:
|
|||||||
* Support for plain C99/C11 code (struct and units relationships) - [_example_](docs/test_cases/t00057.md)
|
* Support for plain C99/C11 code (struct and units relationships) - [_example_](docs/test_cases/t00057.md)
|
||||||
* C++20 concept constraints - [_example_](docs/test_cases/t00059.md)
|
* C++20 concept constraints - [_example_](docs/test_cases/t00059.md)
|
||||||
* **Sequence diagram generation**
|
* **Sequence diagram generation**
|
||||||
* Generation of sequence diagram from specific method or function - [_example_](docs/test_cases/t00002.md)
|
* Generation of sequence diagram from specific method or function - [_example_](docs/test_cases/t20001.md)
|
||||||
* Generation of loop and conditional statements - [_example_](docs/test_cases/t20021.md)
|
* Generation of loop and conditional statements - [_example_](docs/test_cases/t20021.md)
|
||||||
* Generation of switch statements - [_example_](docs/test_cases/t20024.md)
|
* Generation of switch statements - [_example_](docs/test_cases/t20024.md)
|
||||||
* Generation of try/catch blocks - [_example_](docs/test_cases/t20023.md)
|
* Generation of try/catch blocks - [_example_](docs/test_cases/t20023.md)
|
||||||
|
|||||||
@@ -849,9 +849,8 @@ bool parse_source_location(const std::string &location_str, std::string &file,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> get_expression_comment(
|
clang::RawComment *get_expression_raw_comment(const clang::SourceManager &sm,
|
||||||
const clang::SourceManager &sm, const clang::ASTContext &context,
|
const clang::ASTContext &context, const clang::Stmt *stmt)
|
||||||
const clang::Stmt *stmt)
|
|
||||||
{
|
{
|
||||||
// First get the first line of the expression
|
// First get the first line of the expression
|
||||||
auto expr_begin = stmt->getSourceRange().getBegin();
|
auto expr_begin = stmt->getSourceRange().getBegin();
|
||||||
@@ -859,17 +858,13 @@ std::optional<std::string> get_expression_comment(
|
|||||||
|
|
||||||
if (!context.Comments.empty())
|
if (!context.Comments.empty())
|
||||||
for (const auto [offset, raw_comment] :
|
for (const auto [offset, raw_comment] :
|
||||||
*context.Comments.getCommentsInFile(sm.getMainFileID())) {
|
*context.Comments.getCommentsInFile(sm.getFileID(expr_begin))) {
|
||||||
|
|
||||||
auto comment =
|
|
||||||
raw_comment->getFormattedText(sm, sm.getDiagnostics());
|
|
||||||
|
|
||||||
const auto comment_end_line = sm.getSpellingLineNumber(
|
const auto comment_end_line = sm.getSpellingLineNumber(
|
||||||
raw_comment->getSourceRange().getEnd());
|
raw_comment->getSourceRange().getEnd());
|
||||||
|
|
||||||
if (expr_begin_line == comment_end_line ||
|
if (expr_begin_line == comment_end_line ||
|
||||||
expr_begin_line == comment_end_line + 1)
|
expr_begin_line == comment_end_line + 1)
|
||||||
return comment;
|
return raw_comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
@@ -282,8 +282,15 @@ clang::QualType dereference(clang::QualType type);
|
|||||||
std::pair<clang::QualType, std::deque<common::model::context>>
|
std::pair<clang::QualType, std::deque<common::model::context>>
|
||||||
consume_type_context(clang::QualType type);
|
consume_type_context(clang::QualType type);
|
||||||
|
|
||||||
std::optional<std::string> get_expression_comment(
|
/**
|
||||||
const clang::SourceManager &sm, const clang::ASTContext &context,
|
* @brief Extract a comment before or next to a statement
|
||||||
const clang::Stmt *stmt);
|
*
|
||||||
|
* @param sm clang::SourceManager reference
|
||||||
|
* @param context clang::ASTContext reference
|
||||||
|
* @param stmt Pointer to the current clang::Stmt
|
||||||
|
* @return Pointer to a clang::RawComment* or nullptr
|
||||||
|
*/
|
||||||
|
clang::RawComment *get_expression_raw_comment(const clang::SourceManager &sm,
|
||||||
|
const clang::ASTContext &context, const clang::Stmt *stmt);
|
||||||
|
|
||||||
} // namespace clanguml::common
|
} // namespace clanguml::common
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ clang::SourceManager &translation_unit_visitor::source_manager() const
|
|||||||
void translation_unit_visitor::process_comment(
|
void translation_unit_visitor::process_comment(
|
||||||
const clang::NamedDecl &decl, clanguml::common::model::decorated_element &e)
|
const clang::NamedDecl &decl, clanguml::common::model::decorated_element &e)
|
||||||
{
|
{
|
||||||
|
|
||||||
assert(comment_visitor_.get() != nullptr);
|
assert(comment_visitor_.get() != nullptr);
|
||||||
|
|
||||||
comment_visitor_->visit(decl, e);
|
comment_visitor_->visit(decl, e);
|
||||||
@@ -67,12 +66,23 @@ void translation_unit_visitor::process_comment(
|
|||||||
const auto *comment =
|
const auto *comment =
|
||||||
decl.getASTContext().getRawCommentForDeclNoCache(&decl);
|
decl.getASTContext().getRawCommentForDeclNoCache(&decl);
|
||||||
|
|
||||||
|
process_comment(comment, decl.getASTContext().getDiagnostics(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void translation_unit_visitor::process_comment(const clang::RawComment *comment,
|
||||||
|
clang::DiagnosticsEngine &de, clanguml::common::model::decorated_element &e)
|
||||||
|
{
|
||||||
if (comment != nullptr) {
|
if (comment != nullptr) {
|
||||||
|
auto [it, inserted] = processed_comments_.emplace(comment);
|
||||||
|
|
||||||
|
if (!inserted)
|
||||||
|
return;
|
||||||
|
|
||||||
// Process clang-uml decorators in the comments
|
// Process clang-uml decorators in the comments
|
||||||
// TODO: Refactor to use standard block comments processable by clang
|
// TODO: Refactor to use standard block comments processable by clang
|
||||||
// comments
|
// comments
|
||||||
e.add_decorators(decorators::parse(comment->getFormattedText(
|
e.add_decorators(
|
||||||
source_manager_, decl.getASTContext().getDiagnostics())));
|
decorators::parse(comment->getFormattedText(source_manager_, de)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* @brief Set source location in diagram element
|
* @brief Process comment directives in comment attached to a declaration
|
||||||
*
|
*
|
||||||
* @param decl Reference to @ref clang::NamedDecl
|
* @param decl Reference to @ref clang::NamedDecl
|
||||||
* @param element Reference to element to be updated
|
* @param element Reference to element to be updated
|
||||||
@@ -110,6 +110,17 @@ protected:
|
|||||||
void process_comment(const clang::NamedDecl &decl,
|
void process_comment(const clang::NamedDecl &decl,
|
||||||
clanguml::common::model::decorated_element &e);
|
clanguml::common::model::decorated_element &e);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Process comment directives in raw comment
|
||||||
|
*
|
||||||
|
* @param comment clang::RawComment pointer
|
||||||
|
* @param de Reference to clang::DiagnosticsEngine
|
||||||
|
* @param element Reference to element to be updated
|
||||||
|
*/
|
||||||
|
void process_comment(const clang::RawComment *comment,
|
||||||
|
clang::DiagnosticsEngine &de,
|
||||||
|
clanguml::common::model::decorated_element &e);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
clang::SourceManager &source_manager_;
|
clang::SourceManager &source_manager_;
|
||||||
|
|
||||||
@@ -118,5 +129,7 @@ private:
|
|||||||
std::filesystem::path relative_to_path_;
|
std::filesystem::path relative_to_path_;
|
||||||
|
|
||||||
std::filesystem::path translation_unit_path_;
|
std::filesystem::path translation_unit_path_;
|
||||||
|
|
||||||
|
std::set<const clang::RawComment *> processed_comments_;
|
||||||
};
|
};
|
||||||
} // namespace clanguml::common::visitor
|
} // namespace clanguml::common::visitor
|
||||||
|
|||||||
@@ -56,12 +56,31 @@ void generator::generate_diagram_type(std::ostream &ostr) const
|
|||||||
void generator::generate_message_comment(
|
void generator::generate_message_comment(
|
||||||
std::ostream &ostr, const model::message &m) const
|
std::ostream &ostr, const model::message &m) const
|
||||||
{
|
{
|
||||||
if (!config().generate_message_comments() || !m.comment())
|
const auto &from = model().get_participant<model::participant>(m.from());
|
||||||
|
if (!from)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const auto &from = model().get_participant<model::participant>(m.from());
|
bool comment_generated_from_note_decorators{false};
|
||||||
|
for (const auto &decorator : m.decorators()) {
|
||||||
|
auto note = std::dynamic_pointer_cast<decorators::note>(decorator);
|
||||||
|
if (note && note->applies_to_diagram(config().name)) {
|
||||||
|
comment_generated_from_note_decorators = true;
|
||||||
|
|
||||||
if (!from)
|
ostr << indent(1) << "note over " << generate_alias(from.value())
|
||||||
|
<< ": ";
|
||||||
|
|
||||||
|
auto formatted_message = util::format_message_comment(
|
||||||
|
note->text, config().message_comment_width());
|
||||||
|
|
||||||
|
util::replace_all(formatted_message, "\n", "<br/>");
|
||||||
|
ostr << formatted_message << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comment_generated_from_note_decorators)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!config().generate_message_comments() || !m.comment())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ostr << indent(1) << "note over " << generate_alias(from.value()) << ": ";
|
ostr << indent(1) << "note over " << generate_alias(from.value()) << ": ";
|
||||||
|
|||||||
@@ -279,12 +279,30 @@ void generator::generate_activity(const activity &a, std::ostream &ostr,
|
|||||||
void generator::generate_message_comment(
|
void generator::generate_message_comment(
|
||||||
std::ostream &ostr, const model::message &m) const
|
std::ostream &ostr, const model::message &m) const
|
||||||
{
|
{
|
||||||
if (!config().generate_message_comments() || !m.comment())
|
const auto &from = model().get_participant<model::participant>(m.from());
|
||||||
|
if (!from)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const auto &from = model().get_participant<model::participant>(m.from());
|
bool comment_generated_from_note_decorators{false};
|
||||||
|
for (const auto &decorator : m.decorators()) {
|
||||||
|
auto note = std::dynamic_pointer_cast<decorators::note>(decorator);
|
||||||
|
if (note && note->applies_to_diagram(config().name)) {
|
||||||
|
comment_generated_from_note_decorators = true;
|
||||||
|
|
||||||
if (!from)
|
ostr << "note over " << generate_alias(from.value()) << '\n';
|
||||||
|
|
||||||
|
ostr << util::format_message_comment(
|
||||||
|
note->text, config().message_comment_width())
|
||||||
|
<< '\n';
|
||||||
|
|
||||||
|
ostr << "end note" << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comment_generated_from_note_decorators)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!config().generate_message_comments() || !m.comment())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ostr << "note over " << generate_alias(from.value()) << '\n';
|
ostr << "note over " << generate_alias(from.value()) << '\n';
|
||||||
|
|||||||
@@ -445,10 +445,11 @@ void diagram::print() const
|
|||||||
const auto &to_participant = *participants_.at(message.to());
|
const auto &to_participant = *participants_.at(message.to());
|
||||||
|
|
||||||
LOG_TRACE(" Message from={}, from_id={}, "
|
LOG_TRACE(" Message from={}, from_id={}, "
|
||||||
"to={}, to_id={}, name={}, type={}",
|
"to={}, to_id={}, name={}, type={}, comment={}",
|
||||||
from_participant.full_name(false), from_participant.id(),
|
from_participant.full_name(false), from_participant.id(),
|
||||||
to_participant.full_name(false), to_participant.id(),
|
to_participant.full_name(false), to_participant.id(),
|
||||||
message.message_name(), to_string(message.type()));
|
message.message_name(), to_string(message.type()),
|
||||||
|
message.comment() ? message.comment().value() : "None");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -613,8 +613,8 @@ bool translation_unit_visitor::TraverseIfStmt(clang::IfStmt *stmt)
|
|||||||
message m{message_t::kElseIf, current_caller_id};
|
message m{message_t::kElseIf, current_caller_id};
|
||||||
set_source_location(*stmt, m);
|
set_source_location(*stmt, m);
|
||||||
m.condition_text(condition_text);
|
m.condition_text(condition_text);
|
||||||
m.set_comment(clanguml::common::get_expression_comment(
|
m.set_comment(get_expression_comment(source_manager(),
|
||||||
source_manager(), *context().get_ast_context(), stmt));
|
*context().get_ast_context(), current_caller_id, stmt));
|
||||||
diagram().add_block_message(std::move(m));
|
diagram().add_block_message(std::move(m));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -623,8 +623,8 @@ bool translation_unit_visitor::TraverseIfStmt(clang::IfStmt *stmt)
|
|||||||
message m{message_t::kIf, current_caller_id};
|
message m{message_t::kIf, current_caller_id};
|
||||||
set_source_location(*stmt, m);
|
set_source_location(*stmt, m);
|
||||||
m.condition_text(condition_text);
|
m.condition_text(condition_text);
|
||||||
m.set_comment(clanguml::common::get_expression_comment(
|
m.set_comment(get_expression_comment(source_manager(),
|
||||||
source_manager(), *context().get_ast_context(), stmt));
|
*context().get_ast_context(), current_caller_id, stmt));
|
||||||
diagram().add_block_message(std::move(m));
|
diagram().add_block_message(std::move(m));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -659,8 +659,8 @@ bool translation_unit_visitor::TraverseWhileStmt(clang::WhileStmt *stmt)
|
|||||||
message m{message_t::kWhile, current_caller_id};
|
message m{message_t::kWhile, current_caller_id};
|
||||||
set_source_location(*stmt, m);
|
set_source_location(*stmt, m);
|
||||||
m.condition_text(condition_text);
|
m.condition_text(condition_text);
|
||||||
m.set_comment(clanguml::common::get_expression_comment(
|
m.set_comment(get_expression_comment(source_manager(),
|
||||||
source_manager(), *context().get_ast_context(), stmt));
|
*context().get_ast_context(), current_caller_id, stmt));
|
||||||
diagram().add_block_message(std::move(m));
|
diagram().add_block_message(std::move(m));
|
||||||
}
|
}
|
||||||
RecursiveASTVisitor<translation_unit_visitor>::TraverseWhileStmt(stmt);
|
RecursiveASTVisitor<translation_unit_visitor>::TraverseWhileStmt(stmt);
|
||||||
@@ -691,8 +691,8 @@ bool translation_unit_visitor::TraverseDoStmt(clang::DoStmt *stmt)
|
|||||||
message m{message_t::kDo, current_caller_id};
|
message m{message_t::kDo, current_caller_id};
|
||||||
set_source_location(*stmt, m);
|
set_source_location(*stmt, m);
|
||||||
m.condition_text(condition_text);
|
m.condition_text(condition_text);
|
||||||
m.set_comment(clanguml::common::get_expression_comment(
|
m.set_comment(get_expression_comment(source_manager(),
|
||||||
source_manager(), *context().get_ast_context(), stmt));
|
*context().get_ast_context(), current_caller_id, stmt));
|
||||||
diagram().add_block_message(std::move(m));
|
diagram().add_block_message(std::move(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -725,8 +725,8 @@ bool translation_unit_visitor::TraverseForStmt(clang::ForStmt *stmt)
|
|||||||
set_source_location(*stmt, m);
|
set_source_location(*stmt, m);
|
||||||
m.condition_text(condition_text);
|
m.condition_text(condition_text);
|
||||||
|
|
||||||
m.set_comment(clanguml::common::get_expression_comment(
|
m.set_comment(get_expression_comment(source_manager(),
|
||||||
source_manager(), *context().get_ast_context(), stmt));
|
*context().get_ast_context(), current_caller_id, stmt));
|
||||||
|
|
||||||
diagram().add_block_message(std::move(m));
|
diagram().add_block_message(std::move(m));
|
||||||
}
|
}
|
||||||
@@ -754,8 +754,8 @@ bool translation_unit_visitor::TraverseCXXTryStmt(clang::CXXTryStmt *stmt)
|
|||||||
context().enter_trystmt(stmt);
|
context().enter_trystmt(stmt);
|
||||||
message m{message_t::kTry, current_caller_id};
|
message m{message_t::kTry, current_caller_id};
|
||||||
set_source_location(*stmt, m);
|
set_source_location(*stmt, m);
|
||||||
m.set_comment(clanguml::common::get_expression_comment(
|
m.set_comment(get_expression_comment(source_manager(),
|
||||||
source_manager(), *context().get_ast_context(), stmt));
|
*context().get_ast_context(), current_caller_id, stmt));
|
||||||
diagram().add_block_message(std::move(m));
|
diagram().add_block_message(std::move(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -814,8 +814,8 @@ bool translation_unit_visitor::TraverseCXXForRangeStmt(
|
|||||||
message m{message_t::kFor, current_caller_id};
|
message m{message_t::kFor, current_caller_id};
|
||||||
set_source_location(*stmt, m);
|
set_source_location(*stmt, m);
|
||||||
m.condition_text(condition_text);
|
m.condition_text(condition_text);
|
||||||
m.set_comment(clanguml::common::get_expression_comment(
|
m.set_comment(get_expression_comment(source_manager(),
|
||||||
source_manager(), *context().get_ast_context(), stmt));
|
*context().get_ast_context(), current_caller_id, stmt));
|
||||||
diagram().add_block_message(std::move(m));
|
diagram().add_block_message(std::move(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -841,8 +841,8 @@ bool translation_unit_visitor::TraverseSwitchStmt(clang::SwitchStmt *stmt)
|
|||||||
context().enter_switchstmt(stmt);
|
context().enter_switchstmt(stmt);
|
||||||
model::message m{message_t::kSwitch, current_caller_id};
|
model::message m{message_t::kSwitch, current_caller_id};
|
||||||
set_source_location(*stmt, m);
|
set_source_location(*stmt, m);
|
||||||
m.set_comment(clanguml::common::get_expression_comment(
|
m.set_comment(get_expression_comment(source_manager(),
|
||||||
source_manager(), *context().get_ast_context(), stmt));
|
*context().get_ast_context(), current_caller_id, stmt));
|
||||||
diagram().add_block_message(std::move(m));
|
diagram().add_block_message(std::move(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -909,8 +909,8 @@ bool translation_unit_visitor::TraverseConditionalOperator(
|
|||||||
model::message m{message_t::kConditional, current_caller_id};
|
model::message m{message_t::kConditional, current_caller_id};
|
||||||
set_source_location(*stmt, m);
|
set_source_location(*stmt, m);
|
||||||
m.condition_text(condition_text);
|
m.condition_text(condition_text);
|
||||||
m.set_comment(clanguml::common::get_expression_comment(
|
m.set_comment(get_expression_comment(source_manager(),
|
||||||
source_manager(), *context().get_ast_context(), stmt));
|
*context().get_ast_context(), current_caller_id, stmt));
|
||||||
diagram().add_block_message(std::move(m));
|
diagram().add_block_message(std::move(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -960,6 +960,13 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
|
|||||||
|
|
||||||
set_source_location(*expr, m);
|
set_source_location(*expr, m);
|
||||||
|
|
||||||
|
process_comment(clanguml::common::get_expression_raw_comment(
|
||||||
|
source_manager(), *context().get_ast_context(), expr),
|
||||||
|
context().get_ast_context()->getDiagnostics(), m);
|
||||||
|
|
||||||
|
if (m.skip())
|
||||||
|
return true;
|
||||||
|
|
||||||
// If we're currently inside a lambda expression, set it's id as
|
// If we're currently inside a lambda expression, set it's id as
|
||||||
// message source rather then enclosing context
|
// message source rather then enclosing context
|
||||||
// Unless the lambda is declared in a function or method call
|
// Unless the lambda is declared in a function or method call
|
||||||
@@ -1035,8 +1042,8 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m.from() > 0 && m.to() > 0) {
|
if (m.from() > 0 && m.to() > 0) {
|
||||||
auto expr_comment = clanguml::common::get_expression_comment(
|
auto expr_comment = get_expression_comment(source_manager(),
|
||||||
source_manager(), *context().get_ast_context(), expr);
|
*context().get_ast_context(), context().caller_id(), expr);
|
||||||
m.set_comment(expr_comment);
|
m.set_comment(expr_comment);
|
||||||
|
|
||||||
if (diagram().sequences().find(m.from()) ==
|
if (diagram().sequences().find(m.from()) ==
|
||||||
@@ -2519,4 +2526,21 @@ bool translation_unit_visitor::should_include(
|
|||||||
namespace_{decl->getQualifiedNameAsString()}) &&
|
namespace_{decl->getQualifiedNameAsString()}) &&
|
||||||
diagram().should_include(common::model::source_file{decl_file});
|
diagram().should_include(common::model::source_file{decl_file});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> translation_unit_visitor::get_expression_comment(
|
||||||
|
const clang::SourceManager &sm, const clang::ASTContext &context,
|
||||||
|
const int64_t caller_id, const clang::Stmt *stmt)
|
||||||
|
{
|
||||||
|
const auto *raw_comment =
|
||||||
|
clanguml::common::get_expression_raw_comment(sm, context, stmt);
|
||||||
|
|
||||||
|
if (raw_comment == nullptr)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (!processed_comments_.emplace(caller_id, raw_comment).second) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return raw_comment->getFormattedText(sm, sm.getDiagnostics());
|
||||||
|
}
|
||||||
} // namespace clanguml::sequence_diagram::visitor
|
} // namespace clanguml::sequence_diagram::visitor
|
||||||
|
|||||||
@@ -510,6 +510,10 @@ private:
|
|||||||
void pop_message_to_diagram(clang::CallExpr *expr);
|
void pop_message_to_diagram(clang::CallExpr *expr);
|
||||||
void pop_message_to_diagram(clang::CXXConstructExpr *expr);
|
void pop_message_to_diagram(clang::CXXConstructExpr *expr);
|
||||||
|
|
||||||
|
std::optional<std::string> get_expression_comment(
|
||||||
|
const clang::SourceManager &sm, const clang::ASTContext &context,
|
||||||
|
const int64_t caller_id, const clang::Stmt *stmt);
|
||||||
|
|
||||||
// Reference to the output diagram model
|
// Reference to the output diagram model
|
||||||
clanguml::sequence_diagram::model::diagram &diagram_;
|
clanguml::sequence_diagram::model::diagram &diagram_;
|
||||||
|
|
||||||
@@ -546,5 +550,8 @@ private:
|
|||||||
mutable unsigned within_static_variable_declaration_{0};
|
mutable unsigned within_static_variable_declaration_{0};
|
||||||
mutable std::set<const clang::Expr *>
|
mutable std::set<const clang::Expr *>
|
||||||
already_visited_in_static_declaration_{};
|
already_visited_in_static_declaration_{};
|
||||||
|
|
||||||
|
mutable std::set<std::pair<int64_t, const clang::RawComment *>>
|
||||||
|
processed_comments_;
|
||||||
};
|
};
|
||||||
} // namespace clanguml::sequence_diagram::visitor
|
} // namespace clanguml::sequence_diagram::visitor
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
compilation_database_dir: ..
|
compilation_database_dir: ..
|
||||||
output_directory: diagrams
|
output_directory: diagrams
|
||||||
|
add_compile_flags:
|
||||||
|
- -fparse-all-comments
|
||||||
diagrams:
|
diagrams:
|
||||||
t20001_sequence:
|
t20001_sequence:
|
||||||
type: sequence
|
type: sequence
|
||||||
|
|||||||
@@ -63,8 +63,10 @@ int tmain()
|
|||||||
A a;
|
A a;
|
||||||
B b(a);
|
B b(a);
|
||||||
|
|
||||||
|
// \uml{note Just add 2 numbers}
|
||||||
auto tmp = a.add(1, 2);
|
auto tmp = a.add(1, 2);
|
||||||
|
|
||||||
|
// \uml{note[] And now add another 2}
|
||||||
return b.wrap_add3(tmp, 2, 3);
|
return b.wrap_add3(tmp, 2, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,12 @@ TEST_CASE("t20001", "[test-case][sequence]")
|
|||||||
|
|
||||||
REQUIRE_THAT(src, HasComment("t20001 test diagram of type sequence"));
|
REQUIRE_THAT(src, HasComment("t20001 test diagram of type sequence"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(
|
||||||
|
src, HasMessageComment(_A("tmain()"), "Just add 2 numbers"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(
|
||||||
|
src, HasMessageComment(_A("tmain()"), "And now add another 2"));
|
||||||
|
|
||||||
save_puml(config.output_directory(), diagram->name + ".puml", src);
|
save_puml(config.output_directory(), diagram->name + ".puml", src);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|||||||
18
tests/t20038/.clang-uml
Normal file
18
tests/t20038/.clang-uml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
compilation_database_dir: ..
|
||||||
|
output_directory: diagrams
|
||||||
|
add_compile_flags:
|
||||||
|
- -fparse-all-comments
|
||||||
|
diagrams:
|
||||||
|
t20038_sequence:
|
||||||
|
type: sequence
|
||||||
|
glob:
|
||||||
|
- ../../tests/t20038/t20038.cc
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- clanguml::t20038
|
||||||
|
from:
|
||||||
|
- function: clanguml::t20038::tmain()
|
||||||
|
generate_message_comments: true
|
||||||
|
message_comment_width: 35
|
||||||
|
using_namespace:
|
||||||
|
- clanguml::t20038
|
||||||
14
tests/t20038/include/t20038.h
Normal file
14
tests/t20038/include/t20038.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace clanguml {
|
||||||
|
namespace t20038 {
|
||||||
|
|
||||||
|
template <typename T> T add_impl(T a, T b) { return a + b; };
|
||||||
|
|
||||||
|
template <typename T> T add(T a, T b)
|
||||||
|
{
|
||||||
|
// Invoke 'add' implementation
|
||||||
|
return add_impl(a, b);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
91
tests/t20038/t20038.cc
Normal file
91
tests/t20038/t20038.cc
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
#include <future>
|
||||||
|
|
||||||
|
#include "include/t20038.h"
|
||||||
|
|
||||||
|
namespace clanguml {
|
||||||
|
namespace t20038 {
|
||||||
|
|
||||||
|
struct A {
|
||||||
|
int a() { return 1; }
|
||||||
|
|
||||||
|
int aa()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
// Repeat 10 times
|
||||||
|
for (i = 0; i < 10;) {
|
||||||
|
i += a();
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
int aaa() { return 3; }
|
||||||
|
|
||||||
|
int aaaa() { return add(4, 4); }
|
||||||
|
|
||||||
|
int aaaaa() { return 5; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct B {
|
||||||
|
int b() { return a.a(); }
|
||||||
|
|
||||||
|
int bb() { return a.aa(); }
|
||||||
|
|
||||||
|
int bbb() { return a.aaa(); }
|
||||||
|
|
||||||
|
int bbbb() { return a.aaaa(); }
|
||||||
|
|
||||||
|
int bbbbb() { return a.aaaa(); }
|
||||||
|
|
||||||
|
int wrap(int b) { return b; }
|
||||||
|
|
||||||
|
A a;
|
||||||
|
};
|
||||||
|
|
||||||
|
int tmain()
|
||||||
|
{
|
||||||
|
B b;
|
||||||
|
|
||||||
|
// Nisl purus in mollis nunc sed id semper. Varius vel pharetra vel
|
||||||
|
// turpis. Arcu cursus vitae congue mauris rhoncus. Risus feugiat in
|
||||||
|
// ante metus dictum at tempor. Lacus vel facilisis volutpat est. Auctor
|
||||||
|
// urna nunc id cursus metus aliquam. Diam sit amet nisl suscipit
|
||||||
|
// adipiscing. Potenti nullam ac tortor vitae purus faucibus ornare
|
||||||
|
// suspendisse sed. Lobortis feugiat vivamus at augue eget arcu dictum
|
||||||
|
// varius. Non tellus orci ac auctor.
|
||||||
|
if (true) {
|
||||||
|
auto r = 0;
|
||||||
|
// Repeat 5 times...
|
||||||
|
while (r < 5) {
|
||||||
|
r += b.b();
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// ... or just once
|
||||||
|
return 2 * b.b();
|
||||||
|
}
|
||||||
|
|
||||||
|
// \uml{skip}
|
||||||
|
b.bb();
|
||||||
|
|
||||||
|
// TODO: \uml{call B::bbb()}
|
||||||
|
auto bbb_future = std::async(std::launch::deferred, &B::bbb, b);
|
||||||
|
|
||||||
|
bbb_future.wait();
|
||||||
|
|
||||||
|
// This comment should be rendered only once
|
||||||
|
b.wrap(b.bbbb());
|
||||||
|
|
||||||
|
add_impl<double>(2, 2); // What is 2 + 2?
|
||||||
|
|
||||||
|
// This is a generic comment about calling bbbbb()
|
||||||
|
//
|
||||||
|
// \uml{note:some_other_diagram[] This is specific for some_other_diagram}
|
||||||
|
// \uml{note:t20038_sequence[] Calling B::bbbbb()}
|
||||||
|
b.bbbbb();
|
||||||
|
|
||||||
|
// This is a conditional operator
|
||||||
|
return b.bbb() > 5 ? 0 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
127
tests/t20038/test_case.h
Normal file
127
tests/t20038/test_case.h
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
/**
|
||||||
|
* tests/t20038/test_case.h
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021-2023 Bartek Kryza <bkryza@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
TEST_CASE("t20038", "[test-case][sequence]")
|
||||||
|
{
|
||||||
|
auto [config, db] = load_config("t20038");
|
||||||
|
|
||||||
|
auto diagram = config.diagrams["t20038_sequence"];
|
||||||
|
|
||||||
|
REQUIRE(diagram->name == "t20038_sequence");
|
||||||
|
|
||||||
|
auto model = generate_sequence_diagram(*db, diagram);
|
||||||
|
|
||||||
|
REQUIRE(model->name() == "t20038_sequence");
|
||||||
|
|
||||||
|
{
|
||||||
|
auto src = generate_sequence_puml(diagram, *model);
|
||||||
|
AliasMatcher _A(src);
|
||||||
|
|
||||||
|
REQUIRE_THAT(src, StartsWith("@startuml"));
|
||||||
|
REQUIRE_THAT(src, EndsWith("@enduml\n"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(src, HasCall(_A("tmain()"), _A("B"), "b()"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(src, !HasCall(_A("tmain()"), _A("B"), "bb()"));
|
||||||
|
|
||||||
|
// TODO: REQUIRE_THAT(
|
||||||
|
// src, HasCall(_A("tmain()"), _A("B"), "bbb()"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(src, HasCall(_A("tmain()"), _A("B"), "bbbb()"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(src,
|
||||||
|
HasMessageComment(_A("tmain()"),
|
||||||
|
"This comment should be rendered only\\n"
|
||||||
|
"once"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(src,
|
||||||
|
HasCall(_A("tmain()"), _A("add_impl<double>(double,double)"), ""));
|
||||||
|
|
||||||
|
REQUIRE_THAT(
|
||||||
|
src, HasMessageComment(_A("tmain()"), "What is 2 \\+ 2\\?"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(src,
|
||||||
|
!HasMessageComment(
|
||||||
|
_A("tmain()"), "This is specific for some_other_diagram"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(
|
||||||
|
src, HasMessageComment(_A("tmain()"), "Calling B::bbbbb\\(\\)"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(src, HasCall(_A("tmain()"), _A("B"), "bbbbb()"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(src,
|
||||||
|
HasMessageComment(_A("tmain()"), "This is a conditional operator"));
|
||||||
|
|
||||||
|
save_puml(config.output_directory(), diagram->name + ".puml", src);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto j = generate_sequence_json(diagram, *model);
|
||||||
|
|
||||||
|
using namespace json;
|
||||||
|
|
||||||
|
save_json(config.output_directory(), diagram->name + ".json", j);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto src = generate_sequence_mermaid(diagram, *model);
|
||||||
|
|
||||||
|
mermaid::SequenceDiagramAliasMatcher _A(src);
|
||||||
|
using mermaid::HasCall;
|
||||||
|
using mermaid::HasCallInControlCondition;
|
||||||
|
using mermaid::HasMessageComment;
|
||||||
|
|
||||||
|
REQUIRE_THAT(src, HasCall(_A("tmain()"), _A("B"), "b()"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(src, !HasCall(_A("tmain()"), _A("B"), "bb()"));
|
||||||
|
|
||||||
|
// TODO: REQUIRE_THAT(
|
||||||
|
// src, HasCall(_A("tmain()"), _A("B"), "bbb()"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(src, HasCall(_A("tmain()"), _A("B"), "bbbb()"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(src,
|
||||||
|
HasMessageComment(_A("tmain()"),
|
||||||
|
"This comment should be rendered only<br/>"
|
||||||
|
"once"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(src,
|
||||||
|
HasCall(_A("tmain()"), _A("add_impl<double>(double,double)"), ""));
|
||||||
|
|
||||||
|
REQUIRE_THAT(
|
||||||
|
src, HasMessageComment(_A("tmain()"), "What is 2 \\+ 2\\?"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(src,
|
||||||
|
!HasMessageComment(
|
||||||
|
_A("tmain()"), "This is specific for some_other_diagram"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(
|
||||||
|
src, HasMessageComment(_A("tmain()"), "Calling B::bbbbb\\(\\)"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(src, HasCall(_A("tmain()"), _A("B"), "bbbbb()"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(src,
|
||||||
|
!HasMessageComment(
|
||||||
|
_A("tmain()"), "This is specific for some_other_diagram"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(
|
||||||
|
src, HasMessageComment(_A("tmain()"), "Calling B::bbbbb\\(\\)"));
|
||||||
|
|
||||||
|
save_mermaid(config.output_directory(), diagram->name + ".mmd", src);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -429,6 +429,7 @@ using namespace clanguml::test::matchers;
|
|||||||
#include "t20035/test_case.h"
|
#include "t20035/test_case.h"
|
||||||
#include "t20036/test_case.h"
|
#include "t20036/test_case.h"
|
||||||
#include "t20037/test_case.h"
|
#include "t20037/test_case.h"
|
||||||
|
#include "t20038/test_case.h"
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Package diagram tests
|
/// Package diagram tests
|
||||||
|
|||||||
@@ -310,6 +310,9 @@ test_cases:
|
|||||||
- name: t20037
|
- name: t20037
|
||||||
title: Test case checking if activities in static variable declarations appear only once
|
title: Test case checking if activities in static variable declarations appear only once
|
||||||
description:
|
description:
|
||||||
|
- name: t20038
|
||||||
|
title: Sequence diagram comment decorator test case
|
||||||
|
description:
|
||||||
Package diagrams:
|
Package diagrams:
|
||||||
- name: t30001
|
- name: t30001
|
||||||
title: Basic package diagram test case
|
title: Basic package diagram test case
|
||||||
|
|||||||
Reference in New Issue
Block a user