From 1dfade12f028c8037adfae636c31cc04ae381d70 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Sat, 5 Nov 2022 13:52:54 +0100 Subject: [PATCH] Fixed initial function template specialization sequence diagram test case --- src/config/config.cc | 66 ++++++----- src/config/config.h | 4 +- .../visitor/translation_unit_visitor.cc | 109 +++++++++++------- tests/t20004/t20004.cc | 37 +++++- tests/t20004/test_case.h | 18 +++ 5 files changed, 155 insertions(+), 79 deletions(-) diff --git a/src/config/config.cc b/src/config/config.cc index d8e0ffff..a1309431 100644 --- a/src/config/config.cc +++ b/src/config/config.cc @@ -131,6 +131,38 @@ std::vector diagram::get_translation_units( return translation_units; } +void diagram::initialize_type_aliases() +{ + if (!type_aliases().count("std::basic_string")) { + type_aliases().insert({"std::basic_string", "std::string"}); + } + if (!type_aliases().count("std::basic_string,std::allocator>")) { + type_aliases().insert({"std::basic_string,std::allocator>", + "std::string"}); + } + if (!type_aliases().count("std::basic_string")) { + type_aliases().insert({"std::basic_string", "std::wstring"}); + } + if (!type_aliases().count("std::basic_string")) { + type_aliases().insert( + {"std::basic_string", "std::u16string"}); + } + if (!type_aliases().count("std::basic_string")) { + type_aliases().insert( + {"std::basic_string", "std::u32string"}); + } + if (!type_aliases().count("std::integral_constant")) { + type_aliases().insert( + {"std::integral_constant", "std::true_type"}); + } + if (!type_aliases().count("std::integral_constant")) { + type_aliases().insert( + {"std::integral_constant", "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")) { - type_aliases().insert({"std::basic_string", "std::string"}); - } - if (!type_aliases().count("std::basic_string,std::allocator>")) { - type_aliases().insert({"std::basic_string,std::allocator>", - "std::string"}); - } - if (!type_aliases().count("std::basic_string")) { - type_aliases().insert({"std::basic_string", "std::wstring"}); - } - if (!type_aliases().count("std::basic_string")) { - type_aliases().insert( - {"std::basic_string", "std::u16string"}); - } - if (!type_aliases().count("std::basic_string")) { - type_aliases().insert( - {"std::basic_string", "std::u32string"}); - } - if (!type_aliases().count("std::integral_constant")) { - type_aliases().insert( - {"std::integral_constant", "std::true_type"}); - } - if (!type_aliases().count("std::integral_constant")) { - type_aliases().insert( - {"std::integral_constant", "std::false_type"}); - } -} - template <> void append_value(plantuml &l, const plantuml &r) { l.append(r); @@ -590,6 +590,8 @@ template <> struct convert { get_option(node, rhs.start_from); + rhs.initialize_type_aliases(); + return true; } }; diff --git a/src/config/config.h b/src/config/config.h index 7d659b6d..202ffdec 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -156,6 +156,8 @@ struct diagram : public inheritable_diagram_options { std::vector 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{"layout"}; void initialize_relationship_hints(); - - void initialize_type_aliases(); }; struct sequence_diagram : public diagram { diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index 0a8e0ff1..b908a934 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -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( - 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( + 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 f_ptr = - build_function_template_instantiation(*callee_function); + auto callee_name = + callee_function->getQualifiedNameAsString() + "()"; + + std::unique_ptr 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( - 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( - 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; } - } diff --git a/tests/t20004/t20004.cc b/tests/t20004/t20004.cc index 097c43a7..6290ffaf 100644 --- a/tests/t20004/t20004.cc +++ b/tests/t20004/t20004.cc @@ -1,16 +1,49 @@ +#include + namespace clanguml { namespace t20004 { template T m4(T p); +template <> [[maybe_unused]] int m4(int p) { return p + 2; } + +template <> [[maybe_unused]] unsigned long m4(unsigned long p) +{ + return 1000 * p; +} + template T m3(T p) { return m4(p); } template T m2(T p) { return m3(p); } +template <> [[maybe_unused]] std::string m2(std::string p) +{ + return std::string{"m2"} + p; +} + template T m1(T p) { return m2(p); } -template <> [[maybe_unused]] int m4(int p) { return p + 2; } +template <> [[maybe_unused]] float m1(float p) { return 2 * p; } -int main() { return m1(0); } +template <> [[maybe_unused]] unsigned long m1(unsigned long p) +{ + return m4(p); +} + +template <> [[maybe_unused]] std::string m1(std::string p) +{ + return m2(p); +} + +int main() +{ + m1(2.2); + + m1(100); + + m1("main"); + + return m1(0); +} } } diff --git a/tests/t20004/test_case.h b/tests/t20004/test_case.h index 441eb808..0d386f6b 100644 --- a/tests/t20004/test_case.h +++ b/tests/t20004/test_case.h @@ -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()"), "m1")); + REQUIRE_THAT(puml, !HasCall(_A("m1()"), _A("m1()"), "m2")); + REQUIRE_THAT( + puml, !HasCall(_A("m1()"), _A("m1()"), "m2")); + + REQUIRE_THAT(puml, + HasCall(_A("main()"), _A("m1()"), "m1")); + REQUIRE_THAT(puml, + HasCall(_A("m1()"), _A("m4()"), + "m4")); + + REQUIRE_THAT(puml, + HasCall(_A("main()"), _A("m1()"), "m1")); + REQUIRE_THAT(puml, + HasCall(_A("m1()"), _A("m2()"), + "m2")); + REQUIRE_THAT(puml, HasCall(_A("main()"), _A("m1()"), "m1")); + REQUIRE_THAT(puml, HasCall(_A("m1()"), _A("m2()"), "m2")); REQUIRE_THAT(puml, HasCall(_A("m2()"), _A("m3()"), "m3")); REQUIRE_THAT(puml, HasCall(_A("m3()"), _A("m4()"), "m4")); REQUIRE_THAT(puml, EndsWith("@enduml\n"));