Fixed initial function template specialization sequence diagram test case
This commit is contained in:
@@ -131,6 +131,38 @@ std::vector<std::string> diagram::get_translation_units(
|
|||||||
return translation_units;
|
return translation_units;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void diagram::initialize_type_aliases()
|
||||||
|
{
|
||||||
|
if (!type_aliases().count("std::basic_string<char>")) {
|
||||||
|
type_aliases().insert({"std::basic_string<char>", "std::string"});
|
||||||
|
}
|
||||||
|
if (!type_aliases().count("std::basic_string<char,std::char_traits<"
|
||||||
|
"char>,std::allocator<char>>")) {
|
||||||
|
type_aliases().insert({"std::basic_string<char,std::char_traits<"
|
||||||
|
"char>,std::allocator<char>>",
|
||||||
|
"std::string"});
|
||||||
|
}
|
||||||
|
if (!type_aliases().count("std::basic_string<wchar_t>")) {
|
||||||
|
type_aliases().insert({"std::basic_string<wchar_t>", "std::wstring"});
|
||||||
|
}
|
||||||
|
if (!type_aliases().count("std::basic_string<char16_t>")) {
|
||||||
|
type_aliases().insert(
|
||||||
|
{"std::basic_string<char16_t>", "std::u16string"});
|
||||||
|
}
|
||||||
|
if (!type_aliases().count("std::basic_string<char32_t>")) {
|
||||||
|
type_aliases().insert(
|
||||||
|
{"std::basic_string<char32_t>", "std::u32string"});
|
||||||
|
}
|
||||||
|
if (!type_aliases().count("std::integral_constant<bool,true>")) {
|
||||||
|
type_aliases().insert(
|
||||||
|
{"std::integral_constant<bool,true>", "std::true_type"});
|
||||||
|
}
|
||||||
|
if (!type_aliases().count("std::integral_constant<bool,false>")) {
|
||||||
|
type_aliases().insert(
|
||||||
|
{"std::integral_constant<bool,false>", "std::false_type"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
common::model::diagram_t class_diagram::type() const
|
common::model::diagram_t class_diagram::type() const
|
||||||
{
|
{
|
||||||
return common::model::diagram_t::kClass;
|
return common::model::diagram_t::kClass;
|
||||||
@@ -179,38 +211,6 @@ void class_diagram::initialize_relationship_hints()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void class_diagram::initialize_type_aliases()
|
|
||||||
{
|
|
||||||
if (!type_aliases().count("std::basic_string<char>")) {
|
|
||||||
type_aliases().insert({"std::basic_string<char>", "std::string"});
|
|
||||||
}
|
|
||||||
if (!type_aliases().count("std::basic_string<char,std::char_traits<"
|
|
||||||
"char>,std::allocator<char>>")) {
|
|
||||||
type_aliases().insert({"std::basic_string<char,std::char_traits<"
|
|
||||||
"char>,std::allocator<char>>",
|
|
||||||
"std::string"});
|
|
||||||
}
|
|
||||||
if (!type_aliases().count("std::basic_string<wchar_t>")) {
|
|
||||||
type_aliases().insert({"std::basic_string<wchar_t>", "std::wstring"});
|
|
||||||
}
|
|
||||||
if (!type_aliases().count("std::basic_string<char16_t>")) {
|
|
||||||
type_aliases().insert(
|
|
||||||
{"std::basic_string<char16_t>", "std::u16string"});
|
|
||||||
}
|
|
||||||
if (!type_aliases().count("std::basic_string<char32_t>")) {
|
|
||||||
type_aliases().insert(
|
|
||||||
{"std::basic_string<char32_t>", "std::u32string"});
|
|
||||||
}
|
|
||||||
if (!type_aliases().count("std::integral_constant<bool,true>")) {
|
|
||||||
type_aliases().insert(
|
|
||||||
{"std::integral_constant<bool,true>", "std::true_type"});
|
|
||||||
}
|
|
||||||
if (!type_aliases().count("std::integral_constant<bool,false>")) {
|
|
||||||
type_aliases().insert(
|
|
||||||
{"std::integral_constant<bool,false>", "std::false_type"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <> void append_value<plantuml>(plantuml &l, const plantuml &r)
|
template <> void append_value<plantuml>(plantuml &l, const plantuml &r)
|
||||||
{
|
{
|
||||||
l.append(r);
|
l.append(r);
|
||||||
@@ -590,6 +590,8 @@ template <> struct convert<sequence_diagram> {
|
|||||||
|
|
||||||
get_option(node, rhs.start_from);
|
get_option(node, rhs.start_from);
|
||||||
|
|
||||||
|
rhs.initialize_type_aliases();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -156,6 +156,8 @@ struct diagram : public inheritable_diagram_options {
|
|||||||
std::vector<std::string> get_translation_units(
|
std::vector<std::string> get_translation_units(
|
||||||
const std::filesystem::path &root_directory) const;
|
const std::filesystem::path &root_directory) const;
|
||||||
|
|
||||||
|
void initialize_type_aliases();
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -174,8 +176,6 @@ struct class_diagram : public diagram {
|
|||||||
option<layout_hints> layout{"layout"};
|
option<layout_hints> layout{"layout"};
|
||||||
|
|
||||||
void initialize_relationship_hints();
|
void initialize_relationship_hints();
|
||||||
|
|
||||||
void initialize_type_aliases();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sequence_diagram : public diagram {
|
struct sequence_diagram : public diagram {
|
||||||
|
|||||||
@@ -237,32 +237,51 @@ bool translation_unit_visitor::VisitFunctionDecl(clang::FunctionDecl *f)
|
|||||||
if (!diagram().should_include(function_name))
|
if (!diagram().should_include(function_name))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
LOG_DBG("Visiting function declaration {}", function_name);
|
LOG_DBG("Visiting function declaration {} at {}", function_name,
|
||||||
|
f->getLocation().printToString(source_manager()));
|
||||||
|
|
||||||
if (f->isTemplated() && f->getDescribedTemplate()) {
|
if (f->isTemplated()) {
|
||||||
// If the described templated of this function is already in the model
|
if (f->getDescribedTemplate()) {
|
||||||
// skip it:
|
// If the described templated of this function is already in the
|
||||||
if (get_ast_local_id(f->getDescribedTemplate()->getID()))
|
// model skip it:
|
||||||
return true;
|
if (get_ast_local_id(f->getDescribedTemplate()->getID()))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto f_ptr = std::make_unique<sequence_diagram::model::function>(
|
if (f->isFunctionTemplateSpecialization()) {
|
||||||
config().using_namespace());
|
auto f_ptr = build_function_template_instantiation(*f);
|
||||||
|
|
||||||
common::model::namespace_ ns{function_name};
|
f_ptr->set_id(common::to_id(f_ptr->full_name(false)));
|
||||||
f_ptr->set_name(ns.name());
|
|
||||||
ns.pop_back();
|
|
||||||
f_ptr->set_namespace(ns);
|
|
||||||
f_ptr->set_id(common::to_id(function_name));
|
|
||||||
|
|
||||||
call_expression_context_.update(f);
|
call_expression_context_.update(f);
|
||||||
|
|
||||||
call_expression_context_.set_caller_id(f_ptr->id());
|
call_expression_context_.set_caller_id(f_ptr->id());
|
||||||
|
|
||||||
set_ast_local_id(f->getID(), f_ptr->id());
|
set_ast_local_id(f->getID(), f_ptr->id());
|
||||||
|
|
||||||
// TODO: Handle overloaded functions with different arguments
|
// TODO: Handle overloaded functions with different arguments
|
||||||
diagram().add_participant(std::move(f_ptr));
|
diagram().add_participant(std::move(f_ptr));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto f_ptr = std::make_unique<sequence_diagram::model::function>(
|
||||||
|
config().using_namespace());
|
||||||
|
|
||||||
|
common::model::namespace_ ns{function_name};
|
||||||
|
f_ptr->set_name(ns.name());
|
||||||
|
ns.pop_back();
|
||||||
|
f_ptr->set_namespace(ns);
|
||||||
|
f_ptr->set_id(common::to_id(function_name));
|
||||||
|
|
||||||
|
call_expression_context_.update(f);
|
||||||
|
|
||||||
|
call_expression_context_.set_caller_id(f_ptr->id());
|
||||||
|
|
||||||
|
set_ast_local_id(f->getID(), f_ptr->id());
|
||||||
|
|
||||||
|
// TODO: Handle overloaded functions with different arguments
|
||||||
|
diagram().add_participant(std::move(f_ptr));
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -423,34 +442,42 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
bool is_implicit = false;
|
bool is_implicit = false;
|
||||||
if (!get_ast_local_id(callee_function->getID()).has_value()) {
|
auto callee_name =
|
||||||
// This is implicit function template
|
callee_function->getQualifiedNameAsString() + "()";
|
||||||
// specialization/instantiation We have to build it
|
|
||||||
std::unique_ptr<model::function_template> f_ptr =
|
std::unique_ptr<model::function_template> f_ptr;
|
||||||
build_function_template_instantiation(*callee_function);
|
|
||||||
|
//
|
||||||
|
// The target template function is implicit if it's
|
||||||
|
// specialization/instantiation was not explicitly defined
|
||||||
|
// (i.e. it was not added to the diagram by visitor methods)
|
||||||
|
//
|
||||||
|
is_implicit =
|
||||||
|
!get_ast_local_id(callee_function->getID()).has_value();
|
||||||
|
|
||||||
|
//
|
||||||
|
// If the callee is a specialization of a function template,
|
||||||
|
// build it's instantiation model to get the id
|
||||||
|
//
|
||||||
|
if (callee_function->getTemplateSpecializationArgs() &&
|
||||||
|
callee_function->getTemplateSpecializationArgs()->size() > 0) {
|
||||||
|
f_ptr = build_function_template_instantiation(*callee_function);
|
||||||
|
|
||||||
|
callee_name = f_ptr->full_name(false);
|
||||||
|
|
||||||
f_ptr->set_id(common::to_id(f_ptr->full_name(false)));
|
f_ptr->set_id(common::to_id(f_ptr->full_name(false)));
|
||||||
set_ast_local_id(callee_function->getID(), f_ptr->id());
|
set_ast_local_id(callee_function->getID(), f_ptr->id());
|
||||||
diagram().add_participant(std::move(f_ptr));
|
|
||||||
|
|
||||||
// This is not optimal way to check whether the callee
|
|
||||||
// declaration is implicit or explicit (we don't want implicit
|
|
||||||
// declarations as separate participants), but is there a better
|
|
||||||
// way?
|
|
||||||
is_implicit = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_implicit)
|
if (is_implicit)
|
||||||
LOG_DBG("Processing implicit template specialization {}",
|
LOG_DBG("Processing implicit template specialization {}",
|
||||||
diagram()
|
f_ptr->full_name(false));
|
||||||
.get_participant<model::function_template>(
|
|
||||||
get_ast_local_id(callee_function->getID()).value())
|
|
||||||
.value()
|
|
||||||
.full_name(false));
|
|
||||||
|
|
||||||
m.to_name = callee_function->getQualifiedNameAsString();
|
m.to_name = callee_function->getQualifiedNameAsString();
|
||||||
if (is_implicit) {
|
if (is_implicit) {
|
||||||
// If this is an implicit template specialization/instantiation
|
// If this is an implicit template specialization/instantiation
|
||||||
|
// for now we just redirect the call to it's primary template
|
||||||
|
// (TODO: this is not correct in a general case)
|
||||||
m.to = get_ast_local_id(
|
m.to = get_ast_local_id(
|
||||||
callee_function->getPrimaryTemplate()->getID())
|
callee_function->getPrimaryTemplate()->getID())
|
||||||
.value();
|
.value();
|
||||||
@@ -458,14 +485,11 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
|
|||||||
else
|
else
|
||||||
m.to = get_ast_local_id(callee_function->getID()).value();
|
m.to = get_ast_local_id(callee_function->getID()).value();
|
||||||
|
|
||||||
auto message_name =
|
auto message_name = callee_name;
|
||||||
diagram()
|
|
||||||
.get_participant<model::function_template>(
|
|
||||||
get_ast_local_id(callee_function->getID()).value())
|
|
||||||
.value()
|
|
||||||
.full_name(false)
|
|
||||||
.substr();
|
|
||||||
m.message_name = message_name.substr(0, message_name.size() - 2);
|
m.message_name = message_name.substr(0, message_name.size() - 2);
|
||||||
|
|
||||||
|
if (f_ptr)
|
||||||
|
diagram().add_participant(std::move(f_ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &return_type =
|
const auto &return_type =
|
||||||
@@ -908,5 +932,4 @@ bool translation_unit_visitor::simplify_system_template(
|
|||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,49 @@
|
|||||||
|
#include <string>
|
||||||
|
|
||||||
namespace clanguml {
|
namespace clanguml {
|
||||||
namespace t20004 {
|
namespace t20004 {
|
||||||
|
|
||||||
template <typename T> T m4(T p);
|
template <typename T> T m4(T p);
|
||||||
|
|
||||||
|
template <> [[maybe_unused]] int m4<int>(int p) { return p + 2; }
|
||||||
|
|
||||||
|
template <> [[maybe_unused]] unsigned long m4<unsigned long>(unsigned long p)
|
||||||
|
{
|
||||||
|
return 1000 * p;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T> T m3(T p) { return m4<T>(p); }
|
template <typename T> T m3(T p) { return m4<T>(p); }
|
||||||
|
|
||||||
template <typename T> T m2(T p) { return m3<T>(p); }
|
template <typename T> T m2(T p) { return m3<T>(p); }
|
||||||
|
|
||||||
|
template <> [[maybe_unused]] std::string m2<std::string>(std::string p)
|
||||||
|
{
|
||||||
|
return std::string{"m2"} + p;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T> T m1(T p) { return m2<T>(p); }
|
template <typename T> T m1(T p) { return m2<T>(p); }
|
||||||
|
|
||||||
template <> [[maybe_unused]] int m4<int>(int p) { return p + 2; }
|
template <> [[maybe_unused]] float m1<float>(float p) { return 2 * p; }
|
||||||
|
|
||||||
int main() { return m1<int>(0); }
|
template <> [[maybe_unused]] unsigned long m1<unsigned long>(unsigned long p)
|
||||||
|
{
|
||||||
|
return m4<unsigned long>(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <> [[maybe_unused]] std::string m1<std::string>(std::string p)
|
||||||
|
{
|
||||||
|
return m2<std::string>(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
m1<float>(2.2);
|
||||||
|
|
||||||
|
m1<unsigned long>(100);
|
||||||
|
|
||||||
|
m1<std::string>("main");
|
||||||
|
|
||||||
|
return m1<int>(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,25 @@ TEST_CASE("t20004", "[test-case][sequence]")
|
|||||||
AliasMatcher _A(puml);
|
AliasMatcher _A(puml);
|
||||||
|
|
||||||
REQUIRE_THAT(puml, StartsWith("@startuml"));
|
REQUIRE_THAT(puml, StartsWith("@startuml"));
|
||||||
|
REQUIRE_THAT(puml, HasCall(_A("main()"), _A("m1<float>()"), "m1<float>"));
|
||||||
|
REQUIRE_THAT(puml, !HasCall(_A("m1<float>()"), _A("m1<float>()"), "m2<T>"));
|
||||||
|
REQUIRE_THAT(
|
||||||
|
puml, !HasCall(_A("m1<float>()"), _A("m1<float>()"), "m2<float>"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml,
|
||||||
|
HasCall(_A("main()"), _A("m1<unsigned long>()"), "m1<unsigned long>"));
|
||||||
|
REQUIRE_THAT(puml,
|
||||||
|
HasCall(_A("m1<unsigned long>()"), _A("m4<unsigned long>()"),
|
||||||
|
"m4<unsigned long>"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml,
|
||||||
|
HasCall(_A("main()"), _A("m1<std::string>()"), "m1<std::string>"));
|
||||||
|
REQUIRE_THAT(puml,
|
||||||
|
HasCall(_A("m1<std::string>()"), _A("m2<std::string>()"),
|
||||||
|
"m2<std::string>"));
|
||||||
|
|
||||||
REQUIRE_THAT(puml, HasCall(_A("main()"), _A("m1<T>()"), "m1<int>"));
|
REQUIRE_THAT(puml, HasCall(_A("main()"), _A("m1<T>()"), "m1<int>"));
|
||||||
|
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("m2<T>()"), _A("m3<T>()"), "m3<T>"));
|
||||||
REQUIRE_THAT(puml, HasCall(_A("m3<T>()"), _A("m4<T>()"), "m4<T>"));
|
REQUIRE_THAT(puml, HasCall(_A("m3<T>()"), _A("m4<T>()"), "m4<T>"));
|
||||||
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
|
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
|
||||||
|
|||||||
Reference in New Issue
Block a user