Added basic test case for sequence diagrams with template specializations
This commit is contained in:
@@ -68,12 +68,12 @@ int template_trait::calculate_template_specialization_match(
|
|||||||
{
|
{
|
||||||
int res{};
|
int res{};
|
||||||
|
|
||||||
// std::string left = name_and_ns();
|
// std::string left = name_and_ns();
|
||||||
// // TODO: handle variadic templates
|
// // TODO: handle variadic templates
|
||||||
// if ((name_and_ns() != full_name) ||
|
// if ((name_and_ns() != full_name) ||
|
||||||
// (templates().size() != other.templates().size())) {
|
// (templates().size() != other.templates().size())) {
|
||||||
// return res;
|
// return res;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Iterate over all template arguments
|
// Iterate over all template arguments
|
||||||
for (auto i = 0U; i < other.templates().size(); i++) {
|
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(); }
|
bool operator==(const class_ &l, const class_ &r) { return l.id() == r.id(); }
|
||||||
|
|
||||||
|
|
||||||
function::function(const common::model::namespace_ &using_namespace)
|
function::function(const common::model::namespace_ &using_namespace)
|
||||||
: participant{using_namespace}
|
: participant{using_namespace}
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -42,9 +42,14 @@ struct template_trait {
|
|||||||
int calculate_template_specialization_match(
|
int calculate_template_specialization_match(
|
||||||
const template_trait &other, const std::string &full_name) const;
|
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:
|
private:
|
||||||
std::vector<class_diagram::model::template_parameter> templates_;
|
std::vector<class_diagram::model::template_parameter> templates_;
|
||||||
std::string base_template_full_name_;
|
std::string base_template_full_name_;
|
||||||
|
bool is_implicit_{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct participant : public common::model::element,
|
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_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_; }
|
diagram_element::id_t class_id() const { return class_id_; }
|
||||||
|
|
||||||
std::string to_string() const override
|
std::string to_string() const override
|
||||||
@@ -166,6 +183,7 @@ struct method : public participant {
|
|||||||
private:
|
private:
|
||||||
diagram_element::id_t class_id_;
|
diagram_element::id_t class_id_;
|
||||||
std::string method_name_;
|
std::string method_name_;
|
||||||
|
std::string class_full_name_;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct function_template : public participant, public template_trait {
|
struct function_template : public participant, public template_trait {
|
||||||
|
|||||||
@@ -201,9 +201,6 @@ bool translation_unit_visitor::VisitClassTemplateSpecializationDecl(
|
|||||||
if (!diagram().should_include(cls->getQualifiedNameAsString()))
|
if (!diagram().should_include(cls->getQualifiedNameAsString()))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if(cls->isImplicit())
|
|
||||||
LOG_DBG("!!!!!!!!!!!!!!!!!!!!!");
|
|
||||||
|
|
||||||
LOG_DBG("= Visiting template specialization declaration {} at {}",
|
LOG_DBG("= Visiting template specialization declaration {} at {}",
|
||||||
cls->getQualifiedNameAsString(),
|
cls->getQualifiedNameAsString(),
|
||||||
cls->getLocation().printToString(source_manager()));
|
cls->getLocation().printToString(source_manager()));
|
||||||
@@ -249,7 +246,9 @@ bool translation_unit_visitor::VisitClassTemplateSpecializationDecl(
|
|||||||
bool translation_unit_visitor::VisitCXXMethodDecl(clang::CXXMethodDecl *m)
|
bool translation_unit_visitor::VisitCXXMethodDecl(clang::CXXMethodDecl *m)
|
||||||
{
|
{
|
||||||
if (call_expression_context_.current_class_decl_ == nullptr &&
|
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;
|
return true;
|
||||||
|
|
||||||
call_expression_context_.update(m);
|
call_expression_context_.update(m);
|
||||||
@@ -258,20 +257,49 @@ bool translation_unit_visitor::VisitCXXMethodDecl(clang::CXXMethodDecl *m)
|
|||||||
config().using_namespace());
|
config().using_namespace());
|
||||||
|
|
||||||
common::model::namespace_ ns{m->getQualifiedNameAsString()};
|
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();
|
ns.pop_back();
|
||||||
m_ptr->set_name(ns.name());
|
m_ptr->set_name(ns.name());
|
||||||
ns.pop_back();
|
ns.pop_back();
|
||||||
m_ptr->set_namespace(ns);
|
// m_ptr->set_namespace(ns);
|
||||||
|
|
||||||
if (call_expression_context_.current_class_decl_)
|
if (call_expression_context_.current_class_decl_) {
|
||||||
m_ptr->set_class_id(get_ast_local_id(
|
const auto ¤t_class =
|
||||||
call_expression_context_.current_class_decl_->getID())
|
diagram()
|
||||||
.value());
|
.get_participant<model::class_>(get_ast_local_id(
|
||||||
else
|
call_expression_context_.current_class_decl_->getID())
|
||||||
m_ptr->set_class_id(get_ast_local_id(
|
.value())
|
||||||
call_expression_context_.current_class_template_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 ¤t_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 ¤t_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(
|
m_ptr->set_name(
|
||||||
diagram().participants.at(m_ptr->class_id())->full_name_no_ns() +
|
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::activity;
|
||||||
using clanguml::sequence_diagram::model::message;
|
using clanguml::sequence_diagram::model::message;
|
||||||
|
|
||||||
// expr->dump();
|
|
||||||
|
|
||||||
// Skip casts, moves and such
|
// Skip casts, moves and such
|
||||||
if (expr->isCallToStdMove())
|
if (expr->isCallToStdMove())
|
||||||
return true;
|
return true;
|
||||||
@@ -455,30 +481,73 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
|
|||||||
callee_template_specialization->getQualifiedNameAsString());
|
callee_template_specialization->getQualifiedNameAsString());
|
||||||
|
|
||||||
if (!get_ast_local_id(callee_template_specialization->getID())) {
|
if (!get_ast_local_id(callee_template_specialization->getID())) {
|
||||||
callee_template_specialization->dump();
|
call_expression_context context_backup =
|
||||||
|
call_expression_context_;
|
||||||
|
|
||||||
|
// Since this visitor will overwrite the
|
||||||
call_expression_context context_backup = call_expression_context_;
|
// 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(
|
VisitClassTemplateSpecializationDecl(
|
||||||
const_cast<clang::ClassTemplateSpecializationDecl *>(
|
const_cast<clang::ClassTemplateSpecializationDecl *>(
|
||||||
callee_template_specialization));
|
callee_template_specialization));
|
||||||
|
|
||||||
call_expression_context_ = context_backup;
|
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())
|
const auto &participant =
|
||||||
.value();
|
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 {
|
else {
|
||||||
// TODO: The method can be called before it's declaration has been
|
// TODO: The method can be called before it's declaration has been
|
||||||
// encountered by the visitor - for now it's not a problem
|
// encountered by the visitor - for now it's not a problem
|
||||||
// as overloaded methods are not supported
|
// as overloaded methods are not supported
|
||||||
m.to = common::to_id(method_decl->getQualifiedNameAsString());
|
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)
|
m.return_type = method_call_expr->getCallReturnType(current_ast_context)
|
||||||
.getAsString();
|
.getAsString();
|
||||||
|
|
||||||
@@ -555,12 +624,10 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
|
|||||||
for (const auto *decl : unresolved_expr->decls()) {
|
for (const auto *decl : unresolved_expr->decls()) {
|
||||||
if (clang::dyn_cast_or_null<
|
if (clang::dyn_cast_or_null<
|
||||||
clang::FunctionTemplateDecl>(decl)) {
|
clang::FunctionTemplateDecl>(decl)) {
|
||||||
|
// Yes, it's a template
|
||||||
auto *ftd = clang::dyn_cast_or_null<
|
auto *ftd = clang::dyn_cast_or_null<
|
||||||
clang::FunctionTemplateDecl>(decl);
|
clang::FunctionTemplateDecl>(decl);
|
||||||
|
|
||||||
// m.to_name =
|
|
||||||
// to_string(ftd);
|
|
||||||
m.to = get_ast_local_id(ftd->getID()).value();
|
m.to = get_ast_local_id(ftd->getID()).value();
|
||||||
auto message_name =
|
auto message_name =
|
||||||
diagram()
|
diagram()
|
||||||
|
|||||||
@@ -33,9 +33,11 @@ struct call_expression_context {
|
|||||||
call_expression_context()
|
call_expression_context()
|
||||||
: current_class_decl_{nullptr}
|
: current_class_decl_{nullptr}
|
||||||
, current_class_template_decl_{nullptr}
|
, current_class_template_decl_{nullptr}
|
||||||
|
, current_class_template_specialization_decl_{nullptr}
|
||||||
, current_method_decl_{nullptr}
|
, current_method_decl_{nullptr}
|
||||||
, current_function_decl_{nullptr}
|
, current_function_decl_{nullptr}
|
||||||
, current_function_template_decl_{nullptr}
|
, current_function_template_decl_{nullptr}
|
||||||
|
, current_caller_id_{0}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,6 +46,7 @@ struct call_expression_context {
|
|||||||
current_caller_id_ = 0;
|
current_caller_id_ = 0;
|
||||||
current_class_decl_ = nullptr;
|
current_class_decl_ = nullptr;
|
||||||
current_class_template_decl_ = nullptr;
|
current_class_template_decl_ = nullptr;
|
||||||
|
current_class_template_specialization_decl_ = nullptr;
|
||||||
current_method_decl_ = nullptr;
|
current_method_decl_ = nullptr;
|
||||||
current_function_decl_ = nullptr;
|
current_function_decl_ = nullptr;
|
||||||
current_function_template_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_decl_ = {}", (void *)current_class_decl_);
|
||||||
LOG_DBG("current_class_template_decl_ = {}",
|
LOG_DBG("current_class_template_decl_ = {}",
|
||||||
(void *)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_method_decl_ = {}", (void *)current_method_decl_);
|
||||||
LOG_DBG("current_function_decl_ = {}", (void *)current_function_decl_);
|
LOG_DBG("current_function_decl_ = {}", (void *)current_function_decl_);
|
||||||
LOG_DBG("current_function_template_decl_ = {}",
|
LOG_DBG("current_function_template_decl_ = {}",
|
||||||
@@ -65,6 +70,7 @@ struct call_expression_context {
|
|||||||
{
|
{
|
||||||
return (current_class_decl_ != nullptr) ||
|
return (current_class_decl_ != nullptr) ||
|
||||||
(current_class_template_decl_ != nullptr) ||
|
(current_class_template_decl_ != nullptr) ||
|
||||||
|
(current_class_template_specialization_decl_ != nullptr) ||
|
||||||
(current_method_decl_ != nullptr) ||
|
(current_method_decl_ != nullptr) ||
|
||||||
(current_function_decl_ != nullptr) ||
|
(current_function_decl_ != nullptr) ||
|
||||||
(current_function_template_decl_ != nullptr);
|
(current_function_template_decl_ != nullptr);
|
||||||
@@ -72,6 +78,10 @@ struct call_expression_context {
|
|||||||
|
|
||||||
clang::ASTContext *get_ast_context()
|
clang::ASTContext *get_ast_context()
|
||||||
{
|
{
|
||||||
|
if (current_class_template_specialization_decl_)
|
||||||
|
return ¤t_class_template_specialization_decl_
|
||||||
|
->getASTContext();
|
||||||
|
|
||||||
if (current_class_template_decl_)
|
if (current_class_template_decl_)
|
||||||
return ¤t_class_template_decl_->getASTContext();
|
return ¤t_class_template_decl_->getASTContext();
|
||||||
|
|
||||||
@@ -86,6 +96,11 @@ struct call_expression_context {
|
|||||||
|
|
||||||
void update(clang::CXXRecordDecl *cls) { current_class_decl_ = cls; }
|
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)
|
void update(clang::ClassTemplateDecl *clst)
|
||||||
{
|
{
|
||||||
current_class_template_decl_ = clst;
|
current_class_template_decl_ = clst;
|
||||||
@@ -147,6 +162,8 @@ struct call_expression_context {
|
|||||||
|
|
||||||
clang::CXXRecordDecl *current_class_decl_;
|
clang::CXXRecordDecl *current_class_decl_;
|
||||||
clang::ClassTemplateDecl *current_class_template_decl_;
|
clang::ClassTemplateDecl *current_class_template_decl_;
|
||||||
|
clang::ClassTemplateSpecializationDecl
|
||||||
|
*current_class_template_specialization_decl_;
|
||||||
clang::CXXMethodDecl *current_method_decl_;
|
clang::CXXMethodDecl *current_method_decl_;
|
||||||
clang::FunctionDecl *current_function_decl_;
|
clang::FunctionDecl *current_function_decl_;
|
||||||
clang::FunctionTemplateDecl *current_function_template_decl_;
|
clang::FunctionTemplateDecl *current_function_template_decl_;
|
||||||
|
|||||||
@@ -4,17 +4,17 @@ namespace clanguml {
|
|||||||
namespace t20006 {
|
namespace t20006 {
|
||||||
|
|
||||||
template <typename T> struct A {
|
template <typename T> struct A {
|
||||||
T a(T arg) { return arg; }
|
T a_int(T arg) { return arg + 1; }
|
||||||
T a1(T arg) { return arg; }
|
T a_string(T arg) { return arg + "_string"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T> struct B {
|
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_;
|
A<T> a_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <> struct B<std::string> {
|
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_;
|
A<std::string> a_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -35,12 +35,12 @@ TEST_CASE("t20006", "[test-case][sequence]")
|
|||||||
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
|
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
|
||||||
|
|
||||||
// Check if all calls exist
|
// Check if all calls exist
|
||||||
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B<int>"), "b"));
|
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B<T>"), "B<int>::b"));
|
||||||
REQUIRE_THAT(puml, HasCall(_A("B<int>"), _A("A<int>"), "a"));
|
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("tmain()"), _A("B<std::string>"), "b"));
|
||||||
REQUIRE_THAT(
|
REQUIRE_THAT(puml,
|
||||||
puml, !HasCall(_A("B<std::string>"), _A("A<std::string>"), "a"));
|
HasCall(_A("B<std::string>"), _A("A<T>"), "A<std::string>::a_string"));
|
||||||
|
|
||||||
save_puml(
|
save_puml(
|
||||||
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);
|
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);
|
||||||
|
|||||||
Reference in New Issue
Block a user