Added smart pointer dereference sequence diagram test case

This commit is contained in:
Bartek Kryza
2022-11-29 22:08:46 +01:00
parent f1af5460e3
commit 0e3c69ce38
7 changed files with 161 additions and 18 deletions

View File

@@ -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<model::class_>(
get_unique_id(parent_decl->getID()).value())
.value();
get_participant<model::class_>(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) +
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<clanguml::sequence_diagram::model::class_>
translation_unit_visitor::create_class_declaration(clang::CXXRecordDecl *cls)
{

View File

@@ -202,6 +202,29 @@ public:
void finalize() { }
template <typename T = model::participant>
common::optional_ref<T> 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<T>(unique_participant_id.value());
}
template <typename T = model::participant>
common::optional_ref<T> get_participant(
const common::model::diagram_element::id_t id)
{
if (diagram().participants.find(id) == diagram().participants.end())
return {};
return common::optional_ref<T>(
*(static_cast<T *>(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_;
};
}

14
tests/t20009/.clang-uml Normal file
View File

@@ -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()"

25
tests/t20009/t20009.cc Normal file
View File

@@ -0,0 +1,25 @@
#include <memory>
#include <string>
namespace clanguml {
namespace t20009 {
template <typename T> struct A {
void a(T arg) { }
};
template <typename T> struct B {
void b(T arg) { a->a(arg); }
std::unique_ptr<A<T>> a;
};
void tmain()
{
std::shared_ptr<B<std::string>> bstring;
auto bint = std::make_unique<B<int>>();
bstring->b("b");
bint.get()->b(42);
}
}
}

47
tests/t20009/test_case.h Normal file
View File

@@ -0,0 +1,47 @@
/**
* tests/t20009/test_case.h
*
* Copyright (c) 2021-2022 Bartek Kryza <bkryza@gmail.com>
*
* 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<std::string>"), "b"));
REQUIRE_THAT(
puml, HasCall(_A("B<std::string>"), _A("A<std::string>"), "a"));
REQUIRE_THAT(puml, HasCall(_A("tmain()"), _A("B<int>"), "b"));
REQUIRE_THAT(puml, HasCall(_A("B<int>"), _A("A<int>"), "a"));
save_puml(
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);
}

View File

@@ -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

View File

@@ -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