Refactored lambda naming in sequence diagrams

This commit is contained in:
Bartek Kryza
2022-12-04 13:19:44 +01:00
parent f07b35802a
commit b87c6acd44
6 changed files with 98 additions and 32 deletions

View File

@@ -164,6 +164,8 @@ std::string get_source_text(
return get_source_text_raw(printable_range, sm);
}
template <> id_t to_id(const std::string &full_name)
{
return std::hash<std::string>{}(full_name) >> 3;

View File

@@ -36,6 +36,13 @@ generator::generator(
{
}
std::string generator::render_name(std::string name) const
{
util::replace_all(name, "##", "::");
return name;
}
void generator::generate_call(const message &m, std::ostream &ostr) const
{
const auto &from = m_model.get_participant<model::participant>(m.from);
@@ -142,8 +149,8 @@ void generator::generate_participant(std::ostream &ostr, common::id_t id) const
m_model.get_participant<model::participant>(class_id).value();
ostr << "participant \""
<< m_config.using_namespace().relative(
class_participant.full_name(false))
<< render_name(m_config.using_namespace().relative(
class_participant.full_name(false)))
<< "\" as " << class_participant.alias() << '\n';
generated_participants_.emplace(class_id);

View File

@@ -63,6 +63,8 @@ public:
private:
bool is_participant_generated(common::id_t id) const;
std::string render_name(std::string name) const;
mutable std::set<common::id_t> generated_participants_;
};

View File

@@ -68,11 +68,22 @@ call_expression_context &translation_unit_visitor::context()
return call_expression_context_;
}
const call_expression_context &translation_unit_visitor::context() const
{
return call_expression_context_;
}
clanguml::sequence_diagram::model::diagram &translation_unit_visitor::diagram()
{
return diagram_;
}
const clanguml::sequence_diagram::model::diagram &
translation_unit_visitor::diagram() const
{
return diagram_;
}
const clanguml::config::sequence_diagram &
translation_unit_visitor::config() const
{
@@ -920,15 +931,8 @@ translation_unit_visitor::create_class_declaration(clang::CXXRecordDecl *cls)
else if (cls->isLambda()) {
c.is_lambda(true);
if (cls->getParent()) {
auto parent_full_name = get_participant(context().caller_id())
.value()
.full_name_no_ns();
const auto type_name = make_lambda_name(cls);
const auto location = cls->getLocation();
const auto type_name =
fmt::format("{}##(lambda {}:{})", parent_full_name,
source_manager().getSpellingLineNumber(location),
source_manager().getSpellingColumnNumber(location));
c.set_name(type_name);
c.set_namespace(ns);
c.set_id(common::to_id(c.full_name(false)));
@@ -1370,17 +1374,8 @@ void translation_unit_visitor::process_template_specialization_argument(
.full_name(false));
}
else {
auto parent_full_name = get_participant(context().caller_id())
.value()
.full_name_no_ns();
const auto location =
arg.getAsType()->getAsCXXRecordDecl()->getLocation();
const auto type_name =
fmt::format("{}##(lambda {}:{})", parent_full_name,
source_manager().getSpellingLineNumber(location),
source_manager().getSpellingColumnNumber(location));
make_lambda_name(arg.getAsType()->getAsCXXRecordDecl());
argument.set_name(type_name);
}
}
@@ -1680,6 +1675,32 @@ bool translation_unit_visitor::simplify_system_template(
return false;
}
std::string translation_unit_visitor::make_lambda_name(
const clang::CXXRecordDecl *cls) const
{
std::string result;
const auto location = cls->getLocation();
const auto file_line = source_manager().getSpellingLineNumber(location);
const auto file_column = source_manager().getSpellingColumnNumber(location);
const std::string file_name =
util::split(source_manager().getFilename(location).str(), "/").back();
if (context().caller_id() != 0 &&
get_participant(context().caller_id()).has_value()) {
auto parent_full_name =
get_participant(context().caller_id()).value().full_name_no_ns();
result = fmt::format("{}##(lambda {}:{}:{})", parent_full_name,
file_name, file_line, file_column);
}
else {
result =
fmt::format("(lambda {}:{}:{})", file_name, file_line, file_column);
}
return result;
}
void translation_unit_visitor::finalize()
{
for (auto &[id, activity] : diagram().sequences) {

View File

@@ -17,10 +17,10 @@
*/
#pragma once
#include "call_expression_context.h"
#include "common/visitor/translation_unit_visitor.h"
#include "config/config.h"
#include "sequence_diagram/model/diagram.h"
#include "call_expression_context.h"
#include <clang/AST/Expr.h>
#include <clang/AST/RecursiveASTVisitor.h>
@@ -64,10 +64,14 @@ public:
clanguml::sequence_diagram::model::diagram &diagram();
const clanguml::sequence_diagram::model::diagram &diagram() const;
const clanguml::config::sequence_diagram &config() const;
call_expression_context &context();
const call_expression_context &context() const;
void finalize();
template <typename T = model::participant>
@@ -82,6 +86,18 @@ public:
return get_participant<T>(unique_participant_id.value());
}
template <typename T = model::participant>
const common::optional_ref<T> get_participant(const clang::Decl *decl) const
{
assert(decl != nullptr);
auto unique_participant_id = get_unique_id(decl->getID());
if (!unique_participant_id.has_value())
return {};
return get_participant<T>(unique_participant_id.value());
}
template <typename T = model::participant>
common::optional_ref<T> get_participant(
const common::model::diagram_element::id_t id)
@@ -93,6 +109,17 @@ public:
*(static_cast<T *>(diagram().participants.at(id).get())));
}
template <typename T = model::participant>
const common::optional_ref<T> get_participant(
const common::model::diagram_element::id_t id) const
{
if (diagram().participants.find(id) == diagram().participants.end())
return {};
return common::optional_ref<T>(
*(static_cast<T *>(diagram().participants.at(id).get())));
}
/// Store the mapping from local clang entity id (obtained using
/// getID()) method to clang-uml global id
void set_unique_id(
@@ -168,6 +195,8 @@ private:
bool simplify_system_template(class_diagram::model::template_parameter &ct,
const std::string &full_name);
std::string make_lambda_name(const clang::CXXRecordDecl *cls) const;
bool is_smart_pointer(const clang::TemplateDecl *primary_template) const;
bool is_callee_valid_template_specialization(

View File

@@ -36,30 +36,35 @@ TEST_CASE("t20012", "[test-case][sequence]")
// Check if all calls exist
REQUIRE_THAT(puml,
HasCall(_A("tmain()"), _A("tmain()##(lambda 49:20)"), "operator()"));
REQUIRE_THAT(puml, HasCall(_A("tmain()##(lambda 49:20)"), _A("A"), "a"));
HasCall(_A("tmain()"), _A("tmain()::(lambda t20012.cc:49:20)"),
"operator()"));
REQUIRE_THAT(
puml, HasCall(_A("tmain()::(lambda t20012.cc:49:20)"), _A("A"), "a"));
REQUIRE_THAT(puml, HasCall(_A("A"), _A("A"), "aa"));
REQUIRE_THAT(puml, HasCall(_A("A"), _A("A"), "aaa"));
REQUIRE_THAT(puml, HasCall(_A("tmain()##(lambda 49:20)"), _A("B"), "b"));
REQUIRE_THAT(
puml, HasCall(_A("tmain()::(lambda t20012.cc:49:20)"), _A("B"), "b"));
REQUIRE_THAT(puml, HasCall(_A("B"), _A("B"), "bb"));
REQUIRE_THAT(puml, HasCall(_A("B"), _A("B"), "bbb"));
REQUIRE_THAT(puml, HasCall(_A("tmain()##(lambda 62:20)"), _A("C"), "c"));
REQUIRE_THAT(
puml, HasCall(_A("tmain()::(lambda t20012.cc:62:20)"), _A("C"), "c"));
REQUIRE_THAT(puml, HasCall(_A("C"), _A("C"), "cc"));
REQUIRE_THAT(puml, HasCall(_A("C"), _A("C"), "ccc"));
REQUIRE_THAT(puml,
HasCall(_A("tmain()##(lambda 62:20)"), _A("tmain()##(lambda 49:20)"),
"operator()"));
HasCall(_A("tmain()::(lambda t20012.cc:62:20)"),
_A("tmain()::(lambda t20012.cc:49:20)"), "operator()"));
REQUIRE_THAT(puml, HasCall(_A("C"), _A("C"), "ccc"));
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("R<R##(lambda 68:9)>"), "r"));
REQUIRE_THAT(puml,
HasCall(_A("R<R##(lambda 68:9)>"), _A("tmain()##(lambda 68:9)"),
"operator()"));
REQUIRE_THAT(
puml, HasCall(_A("tmain()##(lambda 68:9)"), _A("C"), "c"));
puml, HasCall(_A("tmain()"), _A("R<R::(lambda t20012.cc:68:9)>"), "r"));
REQUIRE_THAT(puml,
HasCall(_A("R<R::(lambda t20012.cc:68:9)>"),
_A("tmain()::(lambda t20012.cc:68:9)"), "operator()"));
REQUIRE_THAT(
puml, HasCall(_A("tmain()::(lambda t20012.cc:68:9)"), _A("C"), "c"));
save_puml(
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);