Added handling of comment decorators (skip, note) in sequence diagram comments (#194)
This commit is contained in:
@@ -849,9 +849,8 @@ bool parse_source_location(const std::string &location_str, std::string &file,
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<std::string> get_expression_comment(
|
||||
const clang::SourceManager &sm, const clang::ASTContext &context,
|
||||
const clang::Stmt *stmt)
|
||||
clang::RawComment *get_expression_raw_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();
|
||||
@@ -859,17 +858,13 @@ std::optional<std::string> get_expression_comment(
|
||||
|
||||
if (!context.Comments.empty())
|
||||
for (const auto [offset, raw_comment] :
|
||||
*context.Comments.getCommentsInFile(sm.getMainFileID())) {
|
||||
|
||||
auto comment =
|
||||
raw_comment->getFormattedText(sm, sm.getDiagnostics());
|
||||
|
||||
*context.Comments.getCommentsInFile(sm.getFileID(expr_begin))) {
|
||||
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 raw_comment;
|
||||
}
|
||||
|
||||
return {};
|
||||
|
||||
@@ -282,8 +282,15 @@ clang::QualType dereference(clang::QualType type);
|
||||
std::pair<clang::QualType, std::deque<common::model::context>>
|
||||
consume_type_context(clang::QualType type);
|
||||
|
||||
std::optional<std::string> get_expression_comment(
|
||||
const clang::SourceManager &sm, const clang::ASTContext &context,
|
||||
const clang::Stmt *stmt);
|
||||
/**
|
||||
* @brief Extract a comment before or next to a statement
|
||||
*
|
||||
* @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
|
||||
|
||||
@@ -59,7 +59,6 @@ clang::SourceManager &translation_unit_visitor::source_manager() const
|
||||
void translation_unit_visitor::process_comment(
|
||||
const clang::NamedDecl &decl, clanguml::common::model::decorated_element &e)
|
||||
{
|
||||
|
||||
assert(comment_visitor_.get() != nullptr);
|
||||
|
||||
comment_visitor_->visit(decl, e);
|
||||
@@ -67,12 +66,23 @@ void translation_unit_visitor::process_comment(
|
||||
const auto *comment =
|
||||
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) {
|
||||
auto [it, inserted] = processed_comments_.emplace(comment);
|
||||
|
||||
if (!inserted)
|
||||
return;
|
||||
|
||||
// Process clang-uml decorators in the comments
|
||||
// TODO: Refactor to use standard block comments processable by clang
|
||||
// comments
|
||||
e.add_decorators(decorators::parse(comment->getFormattedText(
|
||||
source_manager_, decl.getASTContext().getDiagnostics())));
|
||||
e.add_decorators(
|
||||
decorators::parse(comment->getFormattedText(source_manager_, de)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ public:
|
||||
|
||||
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 element Reference to element to be updated
|
||||
@@ -110,6 +110,17 @@ protected:
|
||||
void process_comment(const clang::NamedDecl &decl,
|
||||
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:
|
||||
clang::SourceManager &source_manager_;
|
||||
|
||||
@@ -118,5 +129,7 @@ private:
|
||||
std::filesystem::path relative_to_path_;
|
||||
|
||||
std::filesystem::path translation_unit_path_;
|
||||
|
||||
std::set<const clang::RawComment *> processed_comments_;
|
||||
};
|
||||
} // namespace clanguml::common::visitor
|
||||
|
||||
@@ -56,12 +56,31 @@ void generator::generate_diagram_type(std::ostream &ostr) const
|
||||
void generator::generate_message_comment(
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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(
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
ostr << "note over " << generate_alias(from.value()) << '\n';
|
||||
|
||||
@@ -445,10 +445,11 @@ void diagram::print() const
|
||||
const auto &to_participant = *participants_.at(message.to());
|
||||
|
||||
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(),
|
||||
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};
|
||||
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));
|
||||
m.set_comment(get_expression_comment(source_manager(),
|
||||
*context().get_ast_context(), current_caller_id, stmt));
|
||||
diagram().add_block_message(std::move(m));
|
||||
}
|
||||
else {
|
||||
@@ -623,8 +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));
|
||||
m.set_comment(get_expression_comment(source_manager(),
|
||||
*context().get_ast_context(), current_caller_id, stmt));
|
||||
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};
|
||||
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));
|
||||
m.set_comment(get_expression_comment(source_manager(),
|
||||
*context().get_ast_context(), current_caller_id, stmt));
|
||||
diagram().add_block_message(std::move(m));
|
||||
}
|
||||
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};
|
||||
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));
|
||||
m.set_comment(get_expression_comment(source_manager(),
|
||||
*context().get_ast_context(), current_caller_id, stmt));
|
||||
diagram().add_block_message(std::move(m));
|
||||
}
|
||||
|
||||
@@ -725,8 +725,8 @@ bool translation_unit_visitor::TraverseForStmt(clang::ForStmt *stmt)
|
||||
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));
|
||||
m.set_comment(get_expression_comment(source_manager(),
|
||||
*context().get_ast_context(), current_caller_id, stmt));
|
||||
|
||||
diagram().add_block_message(std::move(m));
|
||||
}
|
||||
@@ -754,8 +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));
|
||||
m.set_comment(get_expression_comment(source_manager(),
|
||||
*context().get_ast_context(), current_caller_id, stmt));
|
||||
diagram().add_block_message(std::move(m));
|
||||
}
|
||||
|
||||
@@ -814,8 +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));
|
||||
m.set_comment(get_expression_comment(source_manager(),
|
||||
*context().get_ast_context(), current_caller_id, stmt));
|
||||
diagram().add_block_message(std::move(m));
|
||||
}
|
||||
|
||||
@@ -841,8 +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));
|
||||
m.set_comment(get_expression_comment(source_manager(),
|
||||
*context().get_ast_context(), current_caller_id, stmt));
|
||||
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};
|
||||
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));
|
||||
m.set_comment(get_expression_comment(source_manager(),
|
||||
*context().get_ast_context(), current_caller_id, stmt));
|
||||
diagram().add_block_message(std::move(m));
|
||||
}
|
||||
|
||||
@@ -960,6 +960,13 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
|
||||
|
||||
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
|
||||
// message source rather then enclosing context
|
||||
// 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) {
|
||||
auto expr_comment = clanguml::common::get_expression_comment(
|
||||
source_manager(), *context().get_ast_context(), expr);
|
||||
auto expr_comment = get_expression_comment(source_manager(),
|
||||
*context().get_ast_context(), context().caller_id(), expr);
|
||||
m.set_comment(expr_comment);
|
||||
|
||||
if (diagram().sequences().find(m.from()) ==
|
||||
@@ -2519,4 +2526,21 @@ bool translation_unit_visitor::should_include(
|
||||
namespace_{decl->getQualifiedNameAsString()}) &&
|
||||
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
|
||||
|
||||
@@ -510,6 +510,10 @@ private:
|
||||
void pop_message_to_diagram(clang::CallExpr *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
|
||||
clanguml::sequence_diagram::model::diagram &diagram_;
|
||||
|
||||
@@ -546,5 +550,8 @@ private:
|
||||
mutable unsigned within_static_variable_declaration_{0};
|
||||
mutable std::set<const clang::Expr *>
|
||||
already_visited_in_static_declaration_{};
|
||||
|
||||
mutable std::set<std::pair<int64_t, const clang::RawComment *>>
|
||||
processed_comments_;
|
||||
};
|
||||
} // namespace clanguml::sequence_diagram::visitor
|
||||
|
||||
Reference in New Issue
Block a user