Added basic test case for sequence diagrams with template specializations

This commit is contained in:
Bartek Kryza
2022-11-23 00:05:43 +01:00
parent 4513e17275
commit b264ef5402
6 changed files with 144 additions and 43 deletions

View File

@@ -68,12 +68,12 @@ int template_trait::calculate_template_specialization_match(
{
int res{};
// std::string left = name_and_ns();
// // TODO: handle variadic templates
// if ((name_and_ns() != full_name) ||
// (templates().size() != other.templates().size())) {
// return res;
// }
// std::string left = name_and_ns();
// // TODO: handle variadic templates
// if ((name_and_ns() != full_name) ||
// (templates().size() != other.templates().size())) {
// return res;
// }
// Iterate over all template arguments
for (auto i = 0U; i < other.templates().size(); i++) {
@@ -156,7 +156,6 @@ std::string class_::full_name(bool relative) const
bool operator==(const class_ &l, const class_ &r) { return l.id() == r.id(); }
function::function(const common::model::namespace_ &using_namespace)
: participant{using_namespace}
{

View File

@@ -42,9 +42,14 @@ struct template_trait {
int calculate_template_specialization_match(
const template_trait &other, const std::string &full_name) const;
bool is_implicit() const { return is_implicit_; }
void set_implicit(bool implicit) { is_implicit_ = implicit; }
private:
std::vector<class_diagram::model::template_parameter> templates_;
std::string base_template_full_name_;
bool is_implicit_{false};
};
struct participant : public common::model::element,
@@ -155,6 +160,18 @@ struct method : public participant {
void set_class_id(diagram_element::id_t id) { class_id_ = id; }
void set_class_full_name(const std::string &name)
{
class_full_name_ = name;
}
const auto &class_full_name() const { return class_full_name_; }
std::string full_name(bool /*relative*/) const override
{
return class_full_name() + "::" + method_name();
}
diagram_element::id_t class_id() const { return class_id_; }
std::string to_string() const override
@@ -166,6 +183,7 @@ struct method : public participant {
private:
diagram_element::id_t class_id_;
std::string method_name_;
std::string class_full_name_;
};
struct function_template : public participant, public template_trait {

View File

@@ -201,9 +201,6 @@ bool translation_unit_visitor::VisitClassTemplateSpecializationDecl(
if (!diagram().should_include(cls->getQualifiedNameAsString()))
return true;
if(cls->isImplicit())
LOG_DBG("!!!!!!!!!!!!!!!!!!!!!");
LOG_DBG("= Visiting template specialization declaration {} at {}",
cls->getQualifiedNameAsString(),
cls->getLocation().printToString(source_manager()));
@@ -249,7 +246,9 @@ bool translation_unit_visitor::VisitClassTemplateSpecializationDecl(
bool translation_unit_visitor::VisitCXXMethodDecl(clang::CXXMethodDecl *m)
{
if (call_expression_context_.current_class_decl_ == nullptr &&
call_expression_context_.current_class_template_decl_ == nullptr)
call_expression_context_.current_class_template_decl_ == nullptr &&
call_expression_context_.current_class_template_specialization_decl_ ==
nullptr)
return true;
call_expression_context_.update(m);
@@ -258,20 +257,49 @@ bool translation_unit_visitor::VisitCXXMethodDecl(clang::CXXMethodDecl *m)
config().using_namespace());
common::model::namespace_ ns{m->getQualifiedNameAsString()};
m_ptr->set_method_name(ns.name());
auto method_name = ns.name();
m_ptr->set_method_name(method_name);
ns.pop_back();
m_ptr->set_name(ns.name());
ns.pop_back();
m_ptr->set_namespace(ns);
// m_ptr->set_namespace(ns);
if (call_expression_context_.current_class_decl_)
m_ptr->set_class_id(get_ast_local_id(
call_expression_context_.current_class_decl_->getID())
.value());
else
m_ptr->set_class_id(get_ast_local_id(
call_expression_context_.current_class_template_decl_->getID())
.value());
if (call_expression_context_.current_class_decl_) {
const auto &current_class =
diagram()
.get_participant<model::class_>(get_ast_local_id(
call_expression_context_.current_class_decl_->getID())
.value())
.value();
m_ptr->set_class_id(current_class.id());
m_ptr->set_class_full_name(current_class.full_name(false));
}
else if (call_expression_context_.current_class_template_decl_) {
const auto &current_template_class =
diagram()
.get_participant<model::class_>(
get_ast_local_id(call_expression_context_
.current_class_template_decl_->getID())
.value())
.value();
m_ptr->set_class_id(current_template_class.id());
m_ptr->set_class_full_name(current_template_class.full_name(false));
}
else {
const auto &current_template_specialization_class =
diagram()
.get_participant<model::class_>(get_ast_local_id(
call_expression_context_
.current_class_template_specialization_decl_->getID())
.value())
.value();
m_ptr->set_class_id(current_template_specialization_class.id());
m_ptr->set_class_full_name(
current_template_specialization_class.full_name(false));
}
m_ptr->set_name(
diagram().participants.at(m_ptr->class_id())->full_name_no_ns() +
@@ -396,8 +424,6 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
using clanguml::sequence_diagram::model::activity;
using clanguml::sequence_diagram::model::message;
// expr->dump();
// Skip casts, moves and such
if (expr->isCallToStdMove())
return true;
@@ -455,30 +481,73 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
callee_template_specialization->getQualifiedNameAsString());
if (!get_ast_local_id(callee_template_specialization->getID())) {
callee_template_specialization->dump();
call_expression_context context_backup =
call_expression_context_;
call_expression_context context_backup = call_expression_context_;
// Since this visitor will overwrite the call_expression_context_
// we need to back it up and restore it later
// Since this visitor will overwrite the
// call_expression_context_ we need to back it up and restore it
// later
VisitClassTemplateSpecializationDecl(
const_cast<clang::ClassTemplateSpecializationDecl *>(
callee_template_specialization));
call_expression_context_ = context_backup;
diagram()
.get_participant<model::class_>(get_ast_local_id(
callee_template_specialization->getID())
.value())
.value()
.set_implicit(true);
}
m.to = get_ast_local_id(callee_template_specialization->getID())
.value();
const auto &participant =
diagram()
.get_participant<model::class_>(get_ast_local_id(
callee_template_specialization->getID())
.value())
.value();
if (participant.is_implicit()) {
const auto *parent_template =
callee_template_specialization->getSpecializedTemplate();
const auto &parent_template_participant =
diagram()
.get_participant<model::class_>(
get_ast_local_id(parent_template->getID()).value())
.value();
const auto parent_method_name =
parent_template_participant.full_name(false) +
"::" + method_decl->getNameAsString();
m.to = common::to_id(parent_method_name);
m.message_name = participant.full_name_no_ns() +
"::" + method_decl->getNameAsString();
}
else {
const auto &specialization_participant =
diagram()
.get_participant<model::class_>(get_ast_local_id(
callee_template_specialization->getID())
.value())
.value();
const auto specialization_method_name =
specialization_participant.full_name(false) +
"::" + method_decl->getNameAsString();
m.to = common::to_id(specialization_method_name);
m.message_name = method_decl->getNameAsString();
}
}
else {
// TODO: The method can be called before it's declaration has been
// encountered by the visitor - for now it's not a problem
// as overloaded methods are not supported
m.to = common::to_id(method_decl->getQualifiedNameAsString());
m.message_name = method_decl->getNameAsString();
}
m.message_name = method_decl->getNameAsString();
m.return_type = method_call_expr->getCallReturnType(current_ast_context)
.getAsString();
@@ -555,12 +624,10 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
for (const auto *decl : unresolved_expr->decls()) {
if (clang::dyn_cast_or_null<
clang::FunctionTemplateDecl>(decl)) {
// Yes, it's a template
auto *ftd = clang::dyn_cast_or_null<
clang::FunctionTemplateDecl>(decl);
// m.to_name =
// to_string(ftd);
m.to = get_ast_local_id(ftd->getID()).value();
auto message_name =
diagram()

View File

@@ -33,9 +33,11 @@ struct call_expression_context {
call_expression_context()
: current_class_decl_{nullptr}
, current_class_template_decl_{nullptr}
, current_class_template_specialization_decl_{nullptr}
, current_method_decl_{nullptr}
, current_function_decl_{nullptr}
, current_function_template_decl_{nullptr}
, current_caller_id_{0}
{
}
@@ -44,6 +46,7 @@ struct call_expression_context {
current_caller_id_ = 0;
current_class_decl_ = nullptr;
current_class_template_decl_ = nullptr;
current_class_template_specialization_decl_ = nullptr;
current_method_decl_ = nullptr;
current_function_decl_ = nullptr;
current_function_template_decl_ = nullptr;
@@ -55,6 +58,8 @@ struct call_expression_context {
LOG_DBG("current_class_decl_ = {}", (void *)current_class_decl_);
LOG_DBG("current_class_template_decl_ = {}",
(void *)current_class_template_decl_);
LOG_DBG("current_class_template_specialization_decl_ = {}",
(void *)current_class_template_specialization_decl_);
LOG_DBG("current_method_decl_ = {}", (void *)current_method_decl_);
LOG_DBG("current_function_decl_ = {}", (void *)current_function_decl_);
LOG_DBG("current_function_template_decl_ = {}",
@@ -65,6 +70,7 @@ struct call_expression_context {
{
return (current_class_decl_ != nullptr) ||
(current_class_template_decl_ != nullptr) ||
(current_class_template_specialization_decl_ != nullptr) ||
(current_method_decl_ != nullptr) ||
(current_function_decl_ != nullptr) ||
(current_function_template_decl_ != nullptr);
@@ -72,6 +78,10 @@ struct call_expression_context {
clang::ASTContext *get_ast_context()
{
if (current_class_template_specialization_decl_)
return &current_class_template_specialization_decl_
->getASTContext();
if (current_class_template_decl_)
return &current_class_template_decl_->getASTContext();
@@ -86,6 +96,11 @@ struct call_expression_context {
void update(clang::CXXRecordDecl *cls) { current_class_decl_ = cls; }
void update(clang::ClassTemplateSpecializationDecl *clst)
{
current_class_template_specialization_decl_ = clst;
}
void update(clang::ClassTemplateDecl *clst)
{
current_class_template_decl_ = clst;
@@ -147,6 +162,8 @@ struct call_expression_context {
clang::CXXRecordDecl *current_class_decl_;
clang::ClassTemplateDecl *current_class_template_decl_;
clang::ClassTemplateSpecializationDecl
*current_class_template_specialization_decl_;
clang::CXXMethodDecl *current_method_decl_;
clang::FunctionDecl *current_function_decl_;
clang::FunctionTemplateDecl *current_function_template_decl_;

View File

@@ -4,17 +4,17 @@ namespace clanguml {
namespace t20006 {
template <typename T> struct A {
T a(T arg) { return arg; }
T a1(T arg) { return arg; }
T a_int(T arg) { return arg + 1; }
T a_string(T arg) { return arg + "_string"; }
};
template <typename T> struct B {
T b(T arg) { return a_.a(arg); }
T b(T arg) { return a_.a_int(arg); }
A<T> a_;
};
template <> struct B<std::string> {
std::string b(std::string arg) { return arg; }
std::string b(std::string arg) { return a_.a_string(arg); }
A<std::string> a_;
};

View File

@@ -35,12 +35,12 @@ TEST_CASE("t20006", "[test-case][sequence]")
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
// Check if all calls exist
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B<int>"), "b"));
REQUIRE_THAT(puml, HasCall(_A("B<int>"), _A("A<int>"), "a"));
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B<T>"), "B<int>::b"));
REQUIRE_THAT(puml, HasCall(_A("B<T>"), _A("A<T>"), "a_int"));
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B<std::string>"), "b"));
REQUIRE_THAT(
puml, !HasCall(_A("B<std::string>"), _A("A<std::string>"), "a"));
REQUIRE_THAT(puml,
HasCall(_A("B<std::string>"), _A("A<T>"), "A<std::string>::a_string"));
save_puml(
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);