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{};
|
||||
|
||||
// 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}
|
||||
{
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 ¤t_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 ¤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(
|
||||
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()
|
||||
|
||||
@@ -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 ¤t_class_template_specialization_decl_
|
||||
->getASTContext();
|
||||
|
||||
if (current_class_template_decl_)
|
||||
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::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_;
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user