Fixed initial function template specialization sequence diagram test case

This commit is contained in:
Bartek Kryza
2022-11-05 13:52:54 +01:00
parent c94d1dea3f
commit 1dfade12f0
5 changed files with 155 additions and 79 deletions

View File

@@ -131,6 +131,38 @@ std::vector<std::string> diagram::get_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
{
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)
{
l.append(r);
@@ -590,6 +590,8 @@ template <> struct convert<sequence_diagram> {
get_option(node, rhs.start_from);
rhs.initialize_type_aliases();
return true;
}
};

View File

@@ -156,6 +156,8 @@ struct diagram : public inheritable_diagram_options {
std::vector<std::string> get_translation_units(
const std::filesystem::path &root_directory) const;
void initialize_type_aliases();
std::string name;
};
@@ -174,8 +176,6 @@ struct class_diagram : public diagram {
option<layout_hints> layout{"layout"};
void initialize_relationship_hints();
void initialize_type_aliases();
};
struct sequence_diagram : public diagram {

View File

@@ -237,32 +237,51 @@ bool translation_unit_visitor::VisitFunctionDecl(clang::FunctionDecl *f)
if (!diagram().should_include(function_name))
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 the described templated of this function is already in the model
// skip it:
if (get_ast_local_id(f->getDescribedTemplate()->getID()))
return true;
if (f->isTemplated()) {
if (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());
if (f->isFunctionTemplateSpecialization()) {
auto f_ptr = build_function_template_instantiation(*f);
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));
f_ptr->set_id(common::to_id(f_ptr->full_name(false)));
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
diagram().add_participant(std::move(f_ptr));
// TODO: Handle overloaded functions with different arguments
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;
}
@@ -423,34 +442,42 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
return true;
bool is_implicit = false;
if (!get_ast_local_id(callee_function->getID()).has_value()) {
// This is implicit function template
// specialization/instantiation We have to build it
std::unique_ptr<model::function_template> f_ptr =
build_function_template_instantiation(*callee_function);
auto callee_name =
callee_function->getQualifiedNameAsString() + "()";
std::unique_ptr<model::function_template> f_ptr;
//
// 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)));
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)
LOG_DBG("Processing implicit template specialization {}",
diagram()
.get_participant<model::function_template>(
get_ast_local_id(callee_function->getID()).value())
.value()
.full_name(false));
f_ptr->full_name(false));
m.to_name = callee_function->getQualifiedNameAsString();
if (is_implicit) {
// 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(
callee_function->getPrimaryTemplate()->getID())
.value();
@@ -458,14 +485,11 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
else
m.to = get_ast_local_id(callee_function->getID()).value();
auto message_name =
diagram()
.get_participant<model::function_template>(
get_ast_local_id(callee_function->getID()).value())
.value()
.full_name(false)
.substr();
auto message_name = callee_name;
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 =
@@ -908,5 +932,4 @@ bool translation_unit_visitor::simplify_system_template(
else
return false;
}
}

View File

@@ -1,16 +1,49 @@
#include <string>
namespace clanguml {
namespace t20004 {
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 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 <> [[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);
}
}
}

View File

@@ -32,7 +32,25 @@ TEST_CASE("t20004", "[test-case][sequence]")
AliasMatcher _A(puml);
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("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>"));
REQUIRE_THAT(puml, EndsWith("@enduml\n"));