diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index 0ff84b37..3b44e8c6 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -279,32 +279,29 @@ bool translation_unit_visitor::VisitCXXMethodDecl(clang::CXXMethodDecl *m) LOG_DBG("Getting method's class with local id {}", parent_decl->getID()); + set_unique_id(m->getID(), m_ptr->id()); + const auto &method_class = - diagram() - .get_participant( - get_unique_id(parent_decl->getID()).value()) - .value(); + get_participant(parent_decl).value(); m_ptr->set_class_id(method_class.id()); m_ptr->set_class_full_name(method_class.full_name(false)); m_ptr->set_name( - diagram().participants.at(m_ptr->class_id())->full_name_no_ns() + + get_participant(m_ptr->class_id()).value().full_name_no_ns() + "::" + m->getNameAsString()); m_ptr->set_id(common::to_id( - diagram().participants.at(m_ptr->class_id())->full_name(false) + + get_participant(m_ptr->class_id()).value().full_name(false) + "::" + m->getNameAsString())); LOG_DBG("Set id {} for method name {}", m_ptr->id(), - diagram().participants.at(m_ptr->class_id())->full_name(false) + + get_participant(m_ptr->class_id()).value().full_name(false) + "::" + m->getNameAsString()); context().update(m); context().set_caller_id(m_ptr->id()); - set_unique_id(m->getID(), m_ptr->id()); - diagram().add_participant(std::move(m_ptr)); return true; @@ -577,14 +574,36 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr) ->getTemplateName() .getAsTemplateDecl(); - auto callee_method_full_name = - diagram() - .participants - .at(get_unique_id(primary_template->getID()) - .value()) - ->full_name(false) + - "::" + - dependent_member_callee->getMember().getAsString(); + std::string callee_method_full_name; + + // First check if the primary template is already in the + // participants map + if (get_participant(primary_template).has_value()) { + callee_method_full_name = + get_participant(primary_template) + .value() + .full_name(false) + + "::" + + dependent_member_callee->getMember().getAsString(); + } + else if (is_smart_pointer(primary_template)) { + // Otherwise check if it a smart pointer + primary_template->getTemplateParameters() + ->asArray() + .front(); + + if (get_participant(primary_template).has_value()) { + callee_method_full_name = + get_participant(primary_template) + .value() + .full_name(false) + + "::" + + dependent_member_callee->getMember() + .getAsString(); + } + else + return true; + } auto callee_id = common::to_id(callee_method_full_name); m.to = callee_id; @@ -696,6 +715,16 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr) return true; } +bool translation_unit_visitor::is_smart_pointer( + const clang::TemplateDecl *primary_template) const +{ + return primary_template->getQualifiedNameAsString().find( + "std::unique_ptr") == 0 || + primary_template->getQualifiedNameAsString().find("std::shared_ptr") == + 0 || + primary_template->getQualifiedNameAsString().find("std::weak_ptr") == 0; +} + std::unique_ptr translation_unit_visitor::create_class_declaration(clang::CXXRecordDecl *cls) { diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.h b/src/sequence_diagram/visitor/translation_unit_visitor.h index 27765512..9d1d2a96 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.h +++ b/src/sequence_diagram/visitor/translation_unit_visitor.h @@ -202,6 +202,29 @@ public: void finalize() { } + template + common::optional_ref get_participant(const clang::Decl *decl) + { + assert(decl != nullptr); + + auto unique_participant_id = get_unique_id(decl->getID()); + if (!unique_participant_id.has_value()) + return {}; + + return get_participant(unique_participant_id.value()); + } + + template + common::optional_ref get_participant( + const common::model::diagram_element::id_t id) + { + if (diagram().participants.find(id) == diagram().participants.end()) + return {}; + + return common::optional_ref( + *(static_cast(diagram().participants.at(id).get()))); + } + /// Store the mapping from local clang entity id (obtained using /// getID()) method to clang-uml global id void set_unique_id( @@ -277,6 +300,8 @@ private: bool simplify_system_template(class_diagram::model::template_parameter &ct, const std::string &full_name); + bool is_smart_pointer(const clang::TemplateDecl *primary_template) const; + // Reference to the output diagram model clanguml::sequence_diagram::model::diagram &diagram_; @@ -296,5 +321,4 @@ private: common::model::access_t>> anonymous_struct_relationships_; }; - } diff --git a/tests/t20009/.clang-uml b/tests/t20009/.clang-uml new file mode 100644 index 00000000..6f8b97ea --- /dev/null +++ b/tests/t20009/.clang-uml @@ -0,0 +1,14 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t20009_sequence: + type: sequence + glob: + - ../../tests/t20009/t20009.cc + include: + namespaces: + - clanguml::t20009 + using_namespace: + - clanguml::t20009 + start_from: + - function: "clanguml::t20009::tmain()" \ No newline at end of file diff --git a/tests/t20009/t20009.cc b/tests/t20009/t20009.cc new file mode 100644 index 00000000..ce9ae973 --- /dev/null +++ b/tests/t20009/t20009.cc @@ -0,0 +1,25 @@ +#include +#include + +namespace clanguml { +namespace t20009 { +template struct A { + void a(T arg) { } +}; + +template struct B { + void b(T arg) { a->a(arg); } + + std::unique_ptr> a; +}; + +void tmain() +{ + std::shared_ptr> bstring; + auto bint = std::make_unique>(); + + bstring->b("b"); + bint.get()->b(42); +} +} +} \ No newline at end of file diff --git a/tests/t20009/test_case.h b/tests/t20009/test_case.h new file mode 100644 index 00000000..9c29d06e --- /dev/null +++ b/tests/t20009/test_case.h @@ -0,0 +1,47 @@ +/** + * tests/t20009/test_case.h + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +TEST_CASE("t20009", "[test-case][sequence]") +{ + auto [config, db] = load_config("t20009"); + + auto diagram = config.diagrams["t20009_sequence"]; + + REQUIRE(diagram->name == "t20009_sequence"); + + auto model = generate_sequence_diagram(*db, diagram); + + REQUIRE(model->name() == "t20009_sequence"); + + auto puml = generate_sequence_puml(diagram, *model); + AliasMatcher _A(puml); + + REQUIRE_THAT(puml, StartsWith("@startuml")); + 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")); + REQUIRE_THAT(puml, HasCall(_A("B"), _A("A"), "a")); + + save_puml( + "./" + config.output_directory() + "/" + diagram->name + ".puml", puml); +} \ No newline at end of file diff --git a/tests/test_cases.cc b/tests/test_cases.cc index 74e7fe9a..1103de0a 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -255,6 +255,7 @@ using namespace clanguml::test::matchers; #include "t20006/test_case.h" #include "t20007/test_case.h" #include "t20008/test_case.h" +#include "t20009/test_case.h" /// /// Package diagram tests diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index 38b74d48..4fa3d254 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -172,6 +172,9 @@ test_cases: - name: t20008 title: Constexpr if sequence diagram test case description: + - name: t20009 + title: Smart pointer dereference call expression test case + description: Package diagrams: - name: t30001 title: Basic package diagram test case