Fixed template function sequence diagram test case
This commit is contained in:
@@ -19,6 +19,50 @@
|
||||
#include "participant.h"
|
||||
|
||||
namespace clanguml::sequence_diagram::model {
|
||||
|
||||
std::ostringstream &template_trait::render_template_params(
|
||||
std::ostringstream &ostr, const common::model::namespace_ &using_namespace,
|
||||
bool relative) const
|
||||
{
|
||||
using clanguml::common::model::namespace_;
|
||||
|
||||
if (!templates_.empty()) {
|
||||
std::vector<std::string> tnames;
|
||||
std::vector<std::string> tnames_simplified;
|
||||
|
||||
std::transform(templates_.cbegin(), templates_.cend(),
|
||||
std::back_inserter(tnames),
|
||||
[ns = using_namespace, relative](
|
||||
const auto &tmplt) { return tmplt.to_string(ns, relative); });
|
||||
|
||||
ostr << fmt::format("<{}>", fmt::join(tnames, ","));
|
||||
}
|
||||
|
||||
return ostr;
|
||||
}
|
||||
|
||||
void template_trait::set_base_template(const std::string &full_name)
|
||||
{
|
||||
base_template_full_name_ = full_name;
|
||||
}
|
||||
|
||||
std::string template_trait::base_template() const
|
||||
{
|
||||
return base_template_full_name_;
|
||||
}
|
||||
|
||||
void template_trait::add_template(
|
||||
class_diagram::model::template_parameter tmplt)
|
||||
{
|
||||
templates_.push_back(std::move(tmplt));
|
||||
}
|
||||
|
||||
const std::vector<class_diagram::model::template_parameter> &
|
||||
template_trait::templates() const
|
||||
{
|
||||
return templates_;
|
||||
}
|
||||
|
||||
class_::class_(const common::model::namespace_ &using_namespace)
|
||||
: participant{using_namespace}
|
||||
{
|
||||
@@ -42,17 +86,6 @@ void class_::is_template_instantiation(bool is_template_instantiation)
|
||||
is_template_instantiation_ = is_template_instantiation;
|
||||
}
|
||||
|
||||
void class_::add_template(class_diagram::model::template_parameter tmplt)
|
||||
{
|
||||
templates_.emplace_back(std::move(tmplt));
|
||||
}
|
||||
|
||||
const std::vector<class_diagram::model::template_parameter> &
|
||||
class_::templates() const
|
||||
{
|
||||
return templates_;
|
||||
}
|
||||
|
||||
std::string class_::full_name_no_ns() const
|
||||
{
|
||||
using namespace clanguml::util;
|
||||
@@ -61,7 +94,7 @@ std::string class_::full_name_no_ns() const
|
||||
|
||||
ostr << name();
|
||||
|
||||
render_template_params(ostr, false);
|
||||
render_template_params(ostr, using_namespace(), false);
|
||||
|
||||
return ostr.str();
|
||||
}
|
||||
@@ -74,7 +107,7 @@ std::string class_::full_name(bool relative) const
|
||||
std::ostringstream ostr;
|
||||
|
||||
ostr << name_and_ns();
|
||||
render_template_params(ostr, relative);
|
||||
render_template_params(ostr, using_namespace(), relative);
|
||||
|
||||
std::string res;
|
||||
|
||||
@@ -89,26 +122,6 @@ std::string class_::full_name(bool relative) const
|
||||
return res;
|
||||
}
|
||||
|
||||
std::ostringstream &class_::render_template_params(
|
||||
std::ostringstream &ostr, bool relative) const
|
||||
{
|
||||
using clanguml::common::model::namespace_;
|
||||
|
||||
if (!templates_.empty()) {
|
||||
std::vector<std::string> tnames;
|
||||
std::vector<std::string> tnames_simplified;
|
||||
|
||||
std::transform(templates_.cbegin(), templates_.cend(),
|
||||
std::back_inserter(tnames),
|
||||
[ns = using_namespace(), relative](
|
||||
const auto &tmplt) { return tmplt.to_string(ns, relative); });
|
||||
|
||||
ostr << fmt::format("<{}>", fmt::join(tnames, ","));
|
||||
}
|
||||
|
||||
return ostr;
|
||||
}
|
||||
|
||||
function::function(const common::model::namespace_ &using_namespace)
|
||||
: participant{using_namespace}
|
||||
{
|
||||
@@ -135,4 +148,51 @@ std::string method::alias() const
|
||||
|
||||
return fmt::format("C_{:022}", class_id_);
|
||||
}
|
||||
|
||||
function_template::function_template(
|
||||
const common::model::namespace_ &using_namespace)
|
||||
: participant{using_namespace}
|
||||
{
|
||||
}
|
||||
|
||||
std::string function_template::full_name(bool relative) const
|
||||
{
|
||||
using namespace clanguml::util;
|
||||
using clanguml::common::model::namespace_;
|
||||
|
||||
std::ostringstream ostr;
|
||||
|
||||
ostr << name_and_ns();
|
||||
render_template_params(ostr, using_namespace(), relative);
|
||||
|
||||
ostr << "()";
|
||||
|
||||
std::string res;
|
||||
|
||||
if (relative)
|
||||
res = using_namespace().relative(ostr.str());
|
||||
else
|
||||
res = ostr.str();
|
||||
|
||||
if (res.empty())
|
||||
return "<<anonymous>>";
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string function_template::full_name_no_ns() const
|
||||
{
|
||||
using namespace clanguml::util;
|
||||
|
||||
std::ostringstream ostr;
|
||||
|
||||
ostr << name();
|
||||
|
||||
render_template_params(ostr, using_namespace(), false);
|
||||
|
||||
ostr << "()";
|
||||
|
||||
return ostr.str();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,6 +26,24 @@
|
||||
|
||||
namespace clanguml::sequence_diagram::model {
|
||||
|
||||
struct template_trait {
|
||||
std::ostringstream &render_template_params(std::ostringstream &ostr,
|
||||
const common::model::namespace_ &using_namespace, bool relative) const;
|
||||
|
||||
void set_base_template(const std::string &full_name);
|
||||
|
||||
std::string base_template() const;
|
||||
|
||||
void add_template(class_diagram::model::template_parameter tmplt);
|
||||
|
||||
const std::vector<class_diagram::model::template_parameter> &
|
||||
templates() const;
|
||||
|
||||
private:
|
||||
std::vector<class_diagram::model::template_parameter> templates_;
|
||||
std::string base_template_full_name_;
|
||||
};
|
||||
|
||||
struct participant : public common::model::element,
|
||||
public common::model::stylable_element {
|
||||
enum class stereotype_t {
|
||||
@@ -50,30 +68,8 @@ struct participant : public common::model::element,
|
||||
|
||||
stereotype_t stereotype_{stereotype_t::participant};
|
||||
};
|
||||
//
|
||||
// struct template_trait {
|
||||
// void set_base_template(const std::string &full_name)
|
||||
// {
|
||||
// base_template_full_name_ = full_name;
|
||||
// }
|
||||
// std::string base_template() const { return base_template_full_name_; }
|
||||
//
|
||||
// void add_template(class_diagram::model::template_parameter tmplt)
|
||||
// {
|
||||
// templates_.push_back(std::move(tmplt));
|
||||
// }
|
||||
//
|
||||
// const std::vector<class_diagram::model::template_parameter> &
|
||||
// templates() const
|
||||
// {
|
||||
// return templates_;
|
||||
// }
|
||||
//
|
||||
// std::vector<class_diagram::model::template_parameter> templates_;
|
||||
// std::string base_template_full_name_;
|
||||
//};
|
||||
|
||||
struct class_ : public participant {
|
||||
struct class_ : public participant, public template_trait {
|
||||
public:
|
||||
class_(const common::model::namespace_ &using_namespace);
|
||||
|
||||
@@ -93,14 +89,6 @@ public:
|
||||
bool is_template_instantiation() const;
|
||||
void is_template_instantiation(bool is_template_instantiation);
|
||||
|
||||
void add_template(class_diagram::model::template_parameter tmplt);
|
||||
|
||||
const std::vector<class_diagram::model::template_parameter> &
|
||||
templates() const;
|
||||
|
||||
void set_base_template(const std::string &full_name);
|
||||
std::string base_template() const;
|
||||
|
||||
friend bool operator==(const class_ &l, const class_ &r);
|
||||
|
||||
std::string full_name(bool relative = true) const override;
|
||||
@@ -113,19 +101,12 @@ public:
|
||||
|
||||
void is_alias(bool alias) { is_alias_ = alias; }
|
||||
|
||||
int calculate_template_specialization_match(
|
||||
const class_ &other, const std::string &full_name) const;
|
||||
|
||||
private:
|
||||
std::ostringstream &render_template_params(
|
||||
std::ostringstream &ostr, bool relative) const;
|
||||
|
||||
bool is_struct_{false};
|
||||
bool is_template_{false};
|
||||
bool is_template_instantiation_{false};
|
||||
bool is_alias_{false};
|
||||
std::vector<class_diagram::model::template_parameter> templates_;
|
||||
std::string base_template_full_name_;
|
||||
|
||||
std::map<std::string, clanguml::class_diagram::model::type_alias>
|
||||
type_aliases_;
|
||||
|
||||
@@ -172,6 +153,19 @@ private:
|
||||
std::string method_name_;
|
||||
};
|
||||
|
||||
struct function_template : public participant { };
|
||||
struct function_template : public participant, public template_trait {
|
||||
function_template(const common::model::namespace_ &using_namespace);
|
||||
|
||||
function_template(const function_template &) = delete;
|
||||
function_template(function_template &&) noexcept = delete;
|
||||
function_template &operator=(const function_template &) = delete;
|
||||
function_template &operator=(function_template &&) = delete;
|
||||
|
||||
std::string type_name() const override { return "function_template"; }
|
||||
|
||||
std::string full_name(bool relative = true) const override;
|
||||
|
||||
std::string full_name_no_ns() const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -243,6 +243,13 @@ bool translation_unit_visitor::VisitFunctionDecl(clang::FunctionDecl *f)
|
||||
|
||||
LOG_DBG("Visiting function declaration {}", function_name);
|
||||
|
||||
if (f->isTemplated() && f->getDescribedTemplate()) {
|
||||
// If the described templated of this function is already in the model
|
||||
// skip it:
|
||||
if (get_ast_local_id(f->getDescribedTemplate()->getID()))
|
||||
return true;
|
||||
}
|
||||
|
||||
auto f_ptr = std::make_unique<sequence_diagram::model::function>(
|
||||
config().using_namespace());
|
||||
|
||||
@@ -272,9 +279,28 @@ bool translation_unit_visitor::VisitFunctionTemplateDecl(
|
||||
if (!diagram().should_include(function_name))
|
||||
return true;
|
||||
|
||||
LOG_DBG("Visiting function template declaration {}", function_name);
|
||||
LOG_DBG("Visiting function template declaration {} at {}", function_name,
|
||||
function_template->getLocation().printToString(source_manager()));
|
||||
|
||||
auto f_ptr = std::make_unique<sequence_diagram::model::function_template>(
|
||||
config().using_namespace());
|
||||
|
||||
common::model::namespace_ ns{function_name};
|
||||
f_ptr->set_name(ns.name());
|
||||
ns.pop_back();
|
||||
f_ptr->set_namespace(ns);
|
||||
|
||||
process_template_parameters(*function_template, *f_ptr);
|
||||
|
||||
f_ptr->set_id(common::to_id(f_ptr->full_name(false)));
|
||||
|
||||
call_expression_context_.update(function_template);
|
||||
call_expression_context_.set_caller_id(f_ptr->id());
|
||||
|
||||
set_ast_local_id(function_template->getID(), f_ptr->id());
|
||||
|
||||
// TODO: Handle overloaded functions with different arguments
|
||||
diagram().add_participant(std::move(f_ptr));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -305,7 +331,7 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
|
||||
m.from_name = diagram().participants.at(m.from)->full_name(false);
|
||||
|
||||
const auto ¤t_ast_context =
|
||||
call_expression_context_.get_ast_context();
|
||||
*call_expression_context_.get_ast_context();
|
||||
|
||||
if (const auto *operator_call_expr =
|
||||
clang::dyn_cast_or_null<clang::CXXOperatorCallExpr>(expr);
|
||||
@@ -372,8 +398,17 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
|
||||
clang::FunctionTemplateDecl>(decl);
|
||||
|
||||
m.to_name = to_string(ftd);
|
||||
m.message_name = to_string(ftd);
|
||||
m.to = get_ast_local_id(ftd->getID()).value();
|
||||
auto message_name =
|
||||
diagram()
|
||||
.get_participant<model::function_template>(
|
||||
m.to)
|
||||
.value()
|
||||
.full_name(false)
|
||||
.substr();
|
||||
m.message_name =
|
||||
message_name.substr(0, message_name.size() - 2);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -392,7 +427,7 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
|
||||
if (!callee_function)
|
||||
return true;
|
||||
|
||||
m.to_name = callee_function->getQualifiedNameAsString() + "()";
|
||||
m.to_name = callee_function->getQualifiedNameAsString();
|
||||
m.message_name = callee_function->getNameAsString();
|
||||
m.to = get_ast_local_id(callee_function->getID()).value();
|
||||
}
|
||||
@@ -523,8 +558,8 @@ translation_unit_visitor::create_class_declaration(clang::CXXRecordDecl *cls)
|
||||
}
|
||||
|
||||
bool translation_unit_visitor::process_template_parameters(
|
||||
const clang::ClassTemplateDecl &template_declaration,
|
||||
sequence_diagram::model::class_ &c)
|
||||
const clang::TemplateDecl &template_declaration,
|
||||
sequence_diagram::model::template_trait &c)
|
||||
{
|
||||
using class_diagram::model::template_parameter;
|
||||
|
||||
|
||||
@@ -61,10 +61,15 @@ struct call_expression_context {
|
||||
current_class_template_decl_ = clst;
|
||||
}
|
||||
|
||||
auto &get_ast_context()
|
||||
clang::ASTContext *get_ast_context()
|
||||
{
|
||||
return current_class_decl_ ? current_class_decl_->getASTContext()
|
||||
: current_function_decl_->getASTContext();
|
||||
if(current_class_decl_)
|
||||
return ¤t_class_decl_->getASTContext();
|
||||
|
||||
if(current_function_template_decl_)
|
||||
return ¤t_function_template_decl_->getASTContext();
|
||||
|
||||
return ¤t_function_decl_->getASTContext();
|
||||
}
|
||||
|
||||
void update(clang::CXXMethodDecl *method) { current_method_decl_ = method; }
|
||||
@@ -241,8 +246,8 @@ private:
|
||||
create_class_declaration(clang::CXXRecordDecl *cls);
|
||||
|
||||
bool process_template_parameters(
|
||||
const clang::ClassTemplateDecl &template_declaration,
|
||||
sequence_diagram::model::class_ &c);
|
||||
const clang::TemplateDecl &template_declaration,
|
||||
sequence_diagram::model::template_trait &c);
|
||||
|
||||
// Reference to the output diagram model
|
||||
clanguml::sequence_diagram::model::diagram &diagram_;
|
||||
|
||||
@@ -34,9 +34,9 @@ TEST_CASE("t20003", "[test-case][sequence]")
|
||||
REQUIRE_THAT(puml, StartsWith("@startuml"));
|
||||
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
|
||||
|
||||
REQUIRE_THAT(puml, HasCall(_A("m1<T>()"), _A("m2<T>()")));
|
||||
REQUIRE_THAT(puml, HasCall(_A("m2<T>()"), _A("m3<T>()")));
|
||||
REQUIRE_THAT(puml, HasCall(_A("m3<T>()"), _A("m4<T>()")));
|
||||
REQUIRE_THAT(puml, HasCall(_A("m1<T>()"), _A("m2<T>()"), "m2<T>"));
|
||||
REQUIRE_THAT(puml, HasCall(_A("m2<T>()"), _A("m3<T>()"), "m3<T>"));
|
||||
REQUIRE_THAT(puml, HasCall(_A("m3<T>()"), _A("m4<T>()"), "m4<T>"));
|
||||
|
||||
save_puml(
|
||||
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);
|
||||
|
||||
Reference in New Issue
Block a user