Added support for call comment directive to inject calls in comments (Fixes #196)
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
* [Customizing participants order](#customizing-participants-order)
|
||||
* [Generating return types](#generating-return-types)
|
||||
* [Generating condition statements](#generating-condition-statements)
|
||||
* [Injecting call expressions manually through comments](#injecting-call-expressions-manually-through-comments)
|
||||
* [Including comments in sequence diagrams](#including-comments-in-sequence-diagrams)
|
||||
|
||||
<!-- tocstop -->
|
||||
@@ -316,6 +317,34 @@ generate_condition_statements: true
|
||||
An example of a diagram with this feature enabled is presented below:
|
||||

|
||||
|
||||
## Injecting call expressions manually through comments
|
||||
In some cases, `clang-uml` is not yet able to discover a call expression target
|
||||
in some line of code. This can include passing function or method address to
|
||||
some executor (e.g. thread), async calls etc.
|
||||
|
||||
However, a call expression can be injected manually through a comment
|
||||
directive `\uml{note CALLEE}`, when placed just before such line of code, for
|
||||
example:
|
||||
|
||||
```cpp
|
||||
// \uml{call clanguml::t20038::B::bbb()}
|
||||
auto bbb_future = std::async(std::launch::deferred, &B::bbb, b);
|
||||
```
|
||||
|
||||
also see the [t20038](test_cases/t20038.md) test case.
|
||||
|
||||
Please note that the callee must have fully qualified name including complete
|
||||
namespace.
|
||||
|
||||
In order to enable this, the `.clang-uml` must contain the following option:
|
||||
|
||||
```yaml
|
||||
add_compile_flags:
|
||||
- -fparse-all-comments
|
||||
```
|
||||
|
||||
otherwise Clang will skip these comments during AST traversal.
|
||||
|
||||
## Including comments in sequence diagrams
|
||||
`clang-uml` can add code comments placed directly before are next to a call
|
||||
expression as notes in the diagram (see for instance
|
||||
|
||||
@@ -856,7 +856,8 @@ clang::RawComment *get_expression_raw_comment(const clang::SourceManager &sm,
|
||||
auto expr_begin = stmt->getSourceRange().getBegin();
|
||||
const auto expr_begin_line = sm.getSpellingLineNumber(expr_begin);
|
||||
|
||||
if (!context.Comments.empty())
|
||||
if (!context.Comments.empty() &&
|
||||
context.Comments.getCommentsInFile(sm.getFileID(expr_begin)) != nullptr)
|
||||
for (const auto [offset, raw_comment] :
|
||||
*context.Comments.getCommentsInFile(sm.getFileID(expr_begin))) {
|
||||
const auto comment_end_line = sm.getSpellingLineNumber(
|
||||
|
||||
@@ -47,6 +47,9 @@ std::shared_ptr<decorator> decorator::from_string(std::string_view c)
|
||||
if (c.find(association::label) == 0) {
|
||||
return association::from_string(c);
|
||||
}
|
||||
if (c.find(call::label) == 0) {
|
||||
return call::from_string(c);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -173,6 +176,17 @@ std::shared_ptr<decorator> association::from_string(std::string_view c)
|
||||
return res;
|
||||
}
|
||||
|
||||
std::shared_ptr<decorator> call::from_string(std::string_view c)
|
||||
{
|
||||
auto res = std::make_shared<call>();
|
||||
auto toks = res->tokenize(call::label, c);
|
||||
|
||||
res->diagrams = toks.diagrams;
|
||||
res->callee = util::trim(toks.text);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<decorator>> parse(
|
||||
std::string documentation_block, const std::string &clanguml_tag)
|
||||
{
|
||||
|
||||
@@ -140,6 +140,17 @@ struct association : public relationship {
|
||||
static std::shared_ptr<decorator> from_string(std::string_view c);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a call message in sequence diagram
|
||||
*/
|
||||
struct call : public decorator {
|
||||
static inline const std::string label{"call"};
|
||||
|
||||
std::string callee;
|
||||
|
||||
static std::shared_ptr<decorator> from_string(std::string_view c);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Parse a documentation block and extract all clang-uml decorators
|
||||
*
|
||||
|
||||
@@ -75,6 +75,10 @@ clang::ASTContext *call_expression_context::get_ast_context() const
|
||||
return ¤t_function_decl_->getASTContext();
|
||||
}
|
||||
|
||||
if (current_method_decl_ != nullptr) {
|
||||
return ¤t_function_decl_->getASTContext();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -947,13 +947,9 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
|
||||
using clanguml::sequence_diagram::model::activity;
|
||||
using clanguml::sequence_diagram::model::message;
|
||||
|
||||
if (!should_include(expr))
|
||||
if (!context().valid() || context().get_ast_context() == nullptr)
|
||||
return true;
|
||||
|
||||
LOG_TRACE("Visiting call expression at {} [caller_id = {}]",
|
||||
expr->getBeginLoc().printToString(source_manager()),
|
||||
context().caller_id());
|
||||
|
||||
message m{message_t::kCall, context().caller_id()};
|
||||
|
||||
m.in_static_declaration_context(within_static_variable_declaration_ > 0);
|
||||
@@ -967,6 +963,25 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
|
||||
if (m.skip())
|
||||
return true;
|
||||
|
||||
auto generated_message_from_comment{false};
|
||||
for (const auto &decorator : m.decorators()) {
|
||||
auto call_decorator =
|
||||
std::dynamic_pointer_cast<decorators::call>(decorator);
|
||||
if (call_decorator &&
|
||||
call_decorator->applies_to_diagram(config().name)) {
|
||||
m.set_to(common::to_id(call_decorator->callee));
|
||||
generated_message_from_comment = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!generated_message_from_comment && !should_include(expr))
|
||||
return true;
|
||||
|
||||
LOG_TRACE("Visiting call expression at {} [caller_id = {}]",
|
||||
expr->getBeginLoc().printToString(source_manager()),
|
||||
context().caller_id());
|
||||
|
||||
// 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
|
||||
@@ -978,17 +993,17 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
|
||||
m.set_message_scope(common::model::message_scope_t::kCondition);
|
||||
}
|
||||
|
||||
if (generated_message_from_comment) { }
|
||||
//
|
||||
// Call to an overloaded operator
|
||||
//
|
||||
if (const auto *operator_call_expr =
|
||||
clang::dyn_cast_or_null<clang::CXXOperatorCallExpr>(expr);
|
||||
operator_call_expr != nullptr) {
|
||||
else if (const auto *operator_call_expr =
|
||||
clang::dyn_cast_or_null<clang::CXXOperatorCallExpr>(expr);
|
||||
operator_call_expr != nullptr) {
|
||||
|
||||
if (!process_operator_call_expression(m, operator_call_expr))
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Call to a class method
|
||||
//
|
||||
@@ -1042,9 +1057,11 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
|
||||
}
|
||||
|
||||
if (m.from() > 0 && m.to() > 0) {
|
||||
auto expr_comment = get_expression_comment(source_manager(),
|
||||
*context().get_ast_context(), context().caller_id(), expr);
|
||||
m.set_comment(expr_comment);
|
||||
if (!generated_message_from_comment) {
|
||||
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()) ==
|
||||
diagram().sequences().end()) {
|
||||
|
||||
@@ -68,7 +68,7 @@ int tmain()
|
||||
// \uml{skip}
|
||||
b.bb();
|
||||
|
||||
// TODO: \uml{call B::bbb()}
|
||||
// \uml{call clanguml::t20038::B::bbb()}
|
||||
auto bbb_future = std::async(std::launch::deferred, &B::bbb, b);
|
||||
|
||||
bbb_future.wait();
|
||||
|
||||
@@ -39,8 +39,7 @@ TEST_CASE("t20038", "[test-case][sequence]")
|
||||
|
||||
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"), "bbb()"));
|
||||
|
||||
REQUIRE_THAT(src, HasCall(_A("tmain()"), _A("B"), "bbbb()"));
|
||||
|
||||
@@ -90,8 +89,7 @@ TEST_CASE("t20038", "[test-case][sequence]")
|
||||
|
||||
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"), "bbb()"));
|
||||
|
||||
REQUIRE_THAT(src, HasCall(_A("tmain()"), _A("B"), "bbbb()"));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user