From b264ef5402d47d11565ba2f7ae03ddc83d30dc8e Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Wed, 23 Nov 2022 00:05:43 +0100 Subject: [PATCH] Added basic test case for sequence diagrams with template specializations --- src/sequence_diagram/model/participant.cc | 13 +- src/sequence_diagram/model/participant.h | 18 +++ .../visitor/translation_unit_visitor.cc | 123 ++++++++++++++---- .../visitor/translation_unit_visitor.h | 17 +++ tests/t20006/t20006.cc | 8 +- tests/t20006/test_case.h | 8 +- 6 files changed, 144 insertions(+), 43 deletions(-) diff --git a/src/sequence_diagram/model/participant.cc b/src/sequence_diagram/model/participant.cc index d787857c..dbe4642c 100644 --- a/src/sequence_diagram/model/participant.cc +++ b/src/sequence_diagram/model/participant.cc @@ -68,12 +68,12 @@ int template_trait::calculate_template_specialization_match( { int res{}; -// std::string left = name_and_ns(); -// // TODO: handle variadic templates -// if ((name_and_ns() != full_name) || -// (templates().size() != other.templates().size())) { -// return res; -// } + // std::string left = name_and_ns(); + // // TODO: handle variadic templates + // if ((name_and_ns() != full_name) || + // (templates().size() != other.templates().size())) { + // return res; + // } // Iterate over all template arguments for (auto i = 0U; i < other.templates().size(); i++) { @@ -156,7 +156,6 @@ std::string class_::full_name(bool relative) const bool operator==(const class_ &l, const class_ &r) { return l.id() == r.id(); } - function::function(const common::model::namespace_ &using_namespace) : participant{using_namespace} { diff --git a/src/sequence_diagram/model/participant.h b/src/sequence_diagram/model/participant.h index a2048ff2..2720bef5 100644 --- a/src/sequence_diagram/model/participant.h +++ b/src/sequence_diagram/model/participant.h @@ -42,9 +42,14 @@ struct template_trait { int calculate_template_specialization_match( const template_trait &other, const std::string &full_name) const; + bool is_implicit() const { return is_implicit_; } + + void set_implicit(bool implicit) { is_implicit_ = implicit; } + private: std::vector templates_; std::string base_template_full_name_; + bool is_implicit_{false}; }; struct participant : public common::model::element, @@ -155,6 +160,18 @@ struct method : public participant { void set_class_id(diagram_element::id_t id) { class_id_ = id; } + void set_class_full_name(const std::string &name) + { + class_full_name_ = name; + } + + const auto &class_full_name() const { return class_full_name_; } + + std::string full_name(bool /*relative*/) const override + { + return class_full_name() + "::" + method_name(); + } + diagram_element::id_t class_id() const { return class_id_; } std::string to_string() const override @@ -166,6 +183,7 @@ struct method : public participant { private: diagram_element::id_t class_id_; std::string method_name_; + std::string class_full_name_; }; struct function_template : public participant, public template_trait { diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index 2bb101ab..8c943089 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -201,9 +201,6 @@ bool translation_unit_visitor::VisitClassTemplateSpecializationDecl( if (!diagram().should_include(cls->getQualifiedNameAsString())) return true; - if(cls->isImplicit()) - LOG_DBG("!!!!!!!!!!!!!!!!!!!!!"); - LOG_DBG("= Visiting template specialization declaration {} at {}", cls->getQualifiedNameAsString(), cls->getLocation().printToString(source_manager())); @@ -249,7 +246,9 @@ bool translation_unit_visitor::VisitClassTemplateSpecializationDecl( bool translation_unit_visitor::VisitCXXMethodDecl(clang::CXXMethodDecl *m) { if (call_expression_context_.current_class_decl_ == nullptr && - call_expression_context_.current_class_template_decl_ == nullptr) + call_expression_context_.current_class_template_decl_ == nullptr && + call_expression_context_.current_class_template_specialization_decl_ == + nullptr) return true; call_expression_context_.update(m); @@ -258,20 +257,49 @@ bool translation_unit_visitor::VisitCXXMethodDecl(clang::CXXMethodDecl *m) config().using_namespace()); common::model::namespace_ ns{m->getQualifiedNameAsString()}; - m_ptr->set_method_name(ns.name()); + auto method_name = ns.name(); + m_ptr->set_method_name(method_name); ns.pop_back(); m_ptr->set_name(ns.name()); ns.pop_back(); - m_ptr->set_namespace(ns); + // m_ptr->set_namespace(ns); - if (call_expression_context_.current_class_decl_) - m_ptr->set_class_id(get_ast_local_id( - call_expression_context_.current_class_decl_->getID()) - .value()); - else - m_ptr->set_class_id(get_ast_local_id( - call_expression_context_.current_class_template_decl_->getID()) - .value()); + if (call_expression_context_.current_class_decl_) { + const auto ¤t_class = + diagram() + .get_participant(get_ast_local_id( + call_expression_context_.current_class_decl_->getID()) + .value()) + .value(); + + m_ptr->set_class_id(current_class.id()); + m_ptr->set_class_full_name(current_class.full_name(false)); + } + else if (call_expression_context_.current_class_template_decl_) { + const auto ¤t_template_class = + diagram() + .get_participant( + get_ast_local_id(call_expression_context_ + .current_class_template_decl_->getID()) + .value()) + .value(); + + m_ptr->set_class_id(current_template_class.id()); + m_ptr->set_class_full_name(current_template_class.full_name(false)); + } + else { + const auto ¤t_template_specialization_class = + diagram() + .get_participant(get_ast_local_id( + call_expression_context_ + .current_class_template_specialization_decl_->getID()) + .value()) + .value(); + + m_ptr->set_class_id(current_template_specialization_class.id()); + m_ptr->set_class_full_name( + current_template_specialization_class.full_name(false)); + } m_ptr->set_name( diagram().participants.at(m_ptr->class_id())->full_name_no_ns() + @@ -396,8 +424,6 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr) using clanguml::sequence_diagram::model::activity; using clanguml::sequence_diagram::model::message; - // expr->dump(); - // Skip casts, moves and such if (expr->isCallToStdMove()) return true; @@ -455,30 +481,73 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr) callee_template_specialization->getQualifiedNameAsString()); if (!get_ast_local_id(callee_template_specialization->getID())) { - callee_template_specialization->dump(); + call_expression_context context_backup = + call_expression_context_; - - call_expression_context context_backup = call_expression_context_; - - // Since this visitor will overwrite the call_expression_context_ - // we need to back it up and restore it later + // Since this visitor will overwrite the + // call_expression_context_ we need to back it up and restore it + // later VisitClassTemplateSpecializationDecl( const_cast( callee_template_specialization)); call_expression_context_ = context_backup; + + diagram() + .get_participant(get_ast_local_id( + callee_template_specialization->getID()) + .value()) + .value() + .set_implicit(true); } - m.to = get_ast_local_id(callee_template_specialization->getID()) - .value(); + const auto &participant = + diagram() + .get_participant(get_ast_local_id( + callee_template_specialization->getID()) + .value()) + .value(); + + if (participant.is_implicit()) { + const auto *parent_template = + callee_template_specialization->getSpecializedTemplate(); + + const auto &parent_template_participant = + diagram() + .get_participant( + get_ast_local_id(parent_template->getID()).value()) + .value(); + + const auto parent_method_name = + parent_template_participant.full_name(false) + + "::" + method_decl->getNameAsString(); + + m.to = common::to_id(parent_method_name); + m.message_name = participant.full_name_no_ns() + + "::" + method_decl->getNameAsString(); + } + else { + const auto &specialization_participant = + diagram() + .get_participant(get_ast_local_id( + callee_template_specialization->getID()) + .value()) + .value(); + const auto specialization_method_name = + specialization_participant.full_name(false) + + "::" + method_decl->getNameAsString(); + + m.to = common::to_id(specialization_method_name); + m.message_name = method_decl->getNameAsString(); + } } else { // TODO: The method can be called before it's declaration has been // encountered by the visitor - for now it's not a problem // as overloaded methods are not supported m.to = common::to_id(method_decl->getQualifiedNameAsString()); + m.message_name = method_decl->getNameAsString(); } - m.message_name = method_decl->getNameAsString(); m.return_type = method_call_expr->getCallReturnType(current_ast_context) .getAsString(); @@ -555,12 +624,10 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr) for (const auto *decl : unresolved_expr->decls()) { if (clang::dyn_cast_or_null< clang::FunctionTemplateDecl>(decl)) { - + // Yes, it's a template auto *ftd = clang::dyn_cast_or_null< clang::FunctionTemplateDecl>(decl); - // m.to_name = - // to_string(ftd); m.to = get_ast_local_id(ftd->getID()).value(); auto message_name = diagram() diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.h b/src/sequence_diagram/visitor/translation_unit_visitor.h index f3cdafa6..d9009c8a 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.h +++ b/src/sequence_diagram/visitor/translation_unit_visitor.h @@ -33,9 +33,11 @@ struct call_expression_context { call_expression_context() : current_class_decl_{nullptr} , current_class_template_decl_{nullptr} + , current_class_template_specialization_decl_{nullptr} , current_method_decl_{nullptr} , current_function_decl_{nullptr} , current_function_template_decl_{nullptr} + , current_caller_id_{0} { } @@ -44,6 +46,7 @@ struct call_expression_context { current_caller_id_ = 0; current_class_decl_ = nullptr; current_class_template_decl_ = nullptr; + current_class_template_specialization_decl_ = nullptr; current_method_decl_ = nullptr; current_function_decl_ = nullptr; current_function_template_decl_ = nullptr; @@ -55,6 +58,8 @@ struct call_expression_context { LOG_DBG("current_class_decl_ = {}", (void *)current_class_decl_); LOG_DBG("current_class_template_decl_ = {}", (void *)current_class_template_decl_); + LOG_DBG("current_class_template_specialization_decl_ = {}", + (void *)current_class_template_specialization_decl_); LOG_DBG("current_method_decl_ = {}", (void *)current_method_decl_); LOG_DBG("current_function_decl_ = {}", (void *)current_function_decl_); LOG_DBG("current_function_template_decl_ = {}", @@ -65,6 +70,7 @@ struct call_expression_context { { return (current_class_decl_ != nullptr) || (current_class_template_decl_ != nullptr) || + (current_class_template_specialization_decl_ != nullptr) || (current_method_decl_ != nullptr) || (current_function_decl_ != nullptr) || (current_function_template_decl_ != nullptr); @@ -72,6 +78,10 @@ struct call_expression_context { clang::ASTContext *get_ast_context() { + if (current_class_template_specialization_decl_) + return ¤t_class_template_specialization_decl_ + ->getASTContext(); + if (current_class_template_decl_) return ¤t_class_template_decl_->getASTContext(); @@ -86,6 +96,11 @@ struct call_expression_context { void update(clang::CXXRecordDecl *cls) { current_class_decl_ = cls; } + void update(clang::ClassTemplateSpecializationDecl *clst) + { + current_class_template_specialization_decl_ = clst; + } + void update(clang::ClassTemplateDecl *clst) { current_class_template_decl_ = clst; @@ -147,6 +162,8 @@ struct call_expression_context { clang::CXXRecordDecl *current_class_decl_; clang::ClassTemplateDecl *current_class_template_decl_; + clang::ClassTemplateSpecializationDecl + *current_class_template_specialization_decl_; clang::CXXMethodDecl *current_method_decl_; clang::FunctionDecl *current_function_decl_; clang::FunctionTemplateDecl *current_function_template_decl_; diff --git a/tests/t20006/t20006.cc b/tests/t20006/t20006.cc index bcd278d6..20729063 100644 --- a/tests/t20006/t20006.cc +++ b/tests/t20006/t20006.cc @@ -4,17 +4,17 @@ namespace clanguml { namespace t20006 { template struct A { - T a(T arg) { return arg; } - T a1(T arg) { return arg; } + T a_int(T arg) { return arg + 1; } + T a_string(T arg) { return arg + "_string"; } }; template struct B { - T b(T arg) { return a_.a(arg); } + T b(T arg) { return a_.a_int(arg); } A a_; }; template <> struct B { - std::string b(std::string arg) { return arg; } + std::string b(std::string arg) { return a_.a_string(arg); } A a_; }; diff --git a/tests/t20006/test_case.h b/tests/t20006/test_case.h index d50da514..9c5e9c8d 100644 --- a/tests/t20006/test_case.h +++ b/tests/t20006/test_case.h @@ -35,12 +35,12 @@ TEST_CASE("t20006", "[test-case][sequence]") REQUIRE_THAT(puml, EndsWith("@enduml\n")); // Check if all calls exist - REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B"), "b")); - REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "a")); + REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B"), "B::b")); + REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "a_int")); REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B"), "b")); - REQUIRE_THAT( - puml, !HasCall(_A("B"), _A("A"), "a")); + REQUIRE_THAT(puml, + HasCall(_A("B"), _A("A"), "A::a_string")); save_puml( "./" + config.output_directory() + "/" + diagram->name + ".puml", puml);