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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"));
|
||||
|
||||
Reference in New Issue
Block a user