Fixed sequence diagram test cases

This commit is contained in:
Bartek Kryza
2022-08-06 12:39:42 +02:00
parent cc0119d079
commit 5cdbb364b0
10 changed files with 159 additions and 87 deletions

View File

@@ -154,7 +154,6 @@ add_library(clang-umllib OBJECT ${SOURCES})
# #
add_executable(clang-uml ${MAIN_SOURCE_FILE}) add_executable(clang-uml ${MAIN_SOURCE_FILE})
target_link_libraries(clang-uml target_link_libraries(clang-uml
# ${LIBCLANG_LIBRARIES}
${YAML_CPP_LIBRARIES} ${YAML_CPP_LIBRARIES}
${LIBTOOLING_LIBS} ${LIBTOOLING_LIBS}
clang-umllib clang-umllib

View File

@@ -44,6 +44,13 @@ void generator::generate_call(const message &m, std::ostream &ostr) const
ostr << '"' << from << "\" " ostr << '"' << from << "\" "
<< common::generators::plantuml::to_plantuml(message_t::kCall) << " \"" << common::generators::plantuml::to_plantuml(message_t::kCall) << " \""
<< to << "\" : " << m.message << "()" << std::endl; << to << "\" : " << m.message << "()" << std::endl;
if (m.message == "add" && to == "A" && from == "A")
LOG_DBG("Generating call '{}' from {} [{}] to {} [{}]", m.message, from,
m.from_usr, to, m.to_usr);
else
LOG_DBG("Generating call '{}' from {} [{}] to {} [{}]", m.message, from,
m.from_usr, to, m.to_usr);
} }
void generator::generate_return(const message &m, std::ostream &ostr) const void generator::generate_return(const message &m, std::ostream &ostr) const
@@ -81,7 +88,7 @@ void generator::generate(std::ostream &ostr) const
for (const auto &sf : m_config.start_from()) { for (const auto &sf : m_config.start_from()) {
if (sf.location_type == source_location::location_t::function) { if (sf.location_type == source_location::location_t::function) {
std::uint_least64_t start_from; std::int64_t start_from;
for (const auto &[k, v] : m_model.sequences) { for (const auto &[k, v] : m_model.sequences) {
if (v.from == sf.location) { if (v.from == sf.location) {
start_from = k; start_from = k;

View File

@@ -47,7 +47,7 @@ public:
bool started{false}; bool started{false};
std::map<std::uint_least64_t, activity> sequences; std::map<int64_t, activity> sequences;
}; };
} }

View File

@@ -29,7 +29,7 @@ struct message {
std::string from; std::string from;
std::uint_least64_t from_usr; std::uint_least64_t from_usr;
std::string to; std::string to;
std::uint_least64_t to_usr; std::int64_t to_usr;
std::string message; std::string message;
std::string return_type; std::string return_type;
unsigned int line; unsigned int line;

View File

@@ -29,106 +29,156 @@ translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm,
: source_manager_{sm} : source_manager_{sm}
, diagram_{diagram} , diagram_{diagram}
, config_{config} , config_{config}
, current_class_decl_{nullptr}
, current_method_decl_{nullptr}
, current_function_decl_{nullptr}
{ {
} }
/* clanguml::sequence_diagram::model::diagram &translation_unit_visitor::diagram()
void translation_unit_visitor::process_activities(const cppast::cpp_function &e)
{ {
return diagram_;
}
for (const auto &function_call_ptr : e.function_calls()) { const clanguml::config::sequence_diagram &
const auto &function_call = translation_unit_visitor::config() const
static_cast<const cpp_member_function_call &>(*function_call_ptr); {
return config_;
}
message m; bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls)
m.type = message_t::kCall; {
current_class_decl_ = cls;
if (!ctx.entity_index() return true;
.lookup_definition(function_call.get_caller_id()) }
.has_value())
continue;
if (!ctx.entity_index() bool translation_unit_visitor::VisitCXXMethodDecl(clang::CXXMethodDecl *method)
.lookup_definition(function_call.get_caller_method_id()) {
.has_value()) current_method_decl_ = method;
continue;
if (!ctx.entity_index() return true;
.lookup_definition(function_call.get_callee_id()) }
.has_value())
continue;
if (!ctx.entity_index() bool translation_unit_visitor::VisitFunctionDecl(
.lookup_definition(function_call.get_callee_method_id()) clang::FunctionDecl *function_declaration)
.has_value()) {
continue; if (!function_declaration->isCXXClassMember())
current_class_decl_ = nullptr;
const auto &caller = current_function_decl_ = function_declaration;
ctx.entity_index()
.lookup_definition(function_call.get_caller_id())
.value();
m.from = cx::util::ns(caller) + "::" + caller.name();
if (!ctx.diagram().should_include( return true;
common::model::namespace_{cx::util::ns(caller)}, caller.name())) }
continue;
if (caller.kind() == cpp_entity_kind::function_t) bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
m.from += "()"; {
using clanguml::common::model::message_t;
using clanguml::common::model::namespace_;
using clanguml::sequence_diagram::model::activity;
using clanguml::sequence_diagram::model::message;
m.from_usr = type_safe::get(function_call.get_caller_method_id()); if (expr->isCallToStdMove())
return true;
const auto &callee = if (expr->isImplicitCXXThis())
ctx.entity_index() return true;
.lookup_definition(function_call.get_callee_id())
.value();
m.to = cx::util::ns(callee) + "::" + callee.name();
if (callee.kind() == cpp_entity_kind::function_t)
m.to += "()";
if (!ctx.diagram().should_include( if (/*clang::dyn_cast_or_null<clang::CXXOperatorCallExpr>(expr) ||*/
common::model::namespace_{cx::util::ns(callee)}, callee.name())) clang::dyn_cast_or_null<clang::ImplicitCastExpr>(expr))
continue; return true;
m.to_usr = type_safe::get(function_call.get_callee_method_id()); if (current_class_decl_ &&
!diagram().should_include(
current_class_decl_->getQualifiedNameAsString()))
return true;
const auto &callee_method = if (current_function_decl_ &&
static_cast<const cppast::cpp_member_function &>( !diagram().should_include(
ctx.entity_index() current_function_decl_->getQualifiedNameAsString()))
.lookup_definition(function_call.get_callee_method_id()) return true;
.value());
m.message = callee_method.name(); message m;
m.type = message_t::kCall;
m.return_type = cppast::to_string(callee_method.return_type()); if (current_class_decl_ != nullptr) {
assert(current_method_decl_ != nullptr);
if (ctx.diagram().sequences.find(m.from_usr) == m.from = current_class_decl_->getQualifiedNameAsString();
ctx.diagram().sequences.end()) { m.from_usr = current_method_decl_->getID();
activity a; }
a.usr = m.from_usr; else {
a.from = m.from; m.from = current_function_decl_->getQualifiedNameAsString() + "()";
ctx.diagram().sequences.insert({m.from_usr, std::move(a)}); m.from_usr = current_function_decl_->getID();
}
LOG_DBG("Adding sequence {} -{}()-> {}", m.from, m.message, m.to);
ctx.diagram().sequences[m.from_usr].messages.emplace_back(std::move(m));
} }
}
void translation_unit_visitor::operator()(const cppast::cpp_entity &file) const auto &current_ast_context = current_class_decl_
{ ? current_class_decl_->getASTContext()
: current_function_decl_->getASTContext();
cppast::visit(file, [&, this](const cpp_entity &e, visitor_info) { if (const auto *operator_call_expr =
if (e.kind() == cpp_entity_kind::function_t) { clang::dyn_cast_or_null<clang::CXXOperatorCallExpr>(expr);
const auto &function = static_cast<const cpp_function &>(e); operator_call_expr != nullptr) {
process_activities(function); [[maybe_unused]] const auto *callee_method_decl =
} operator_call_expr->getCalleeDecl();
else if (e.kind() == cpp_entity_kind::member_function_t) { }
const auto &member_function = static_cast<const cpp_function &>(e); else if (const auto *method_call_expr =
process_activities(member_function); clang::dyn_cast_or_null<clang::CXXMemberCallExpr>(expr);
} method_call_expr != nullptr) {
}); const auto *callee_decl = method_call_expr->getMethodDecl()
? method_call_expr->getMethodDecl()->getParent()
: nullptr;
if (!callee_decl ||
!diagram().should_include(
/*namespace_{*/ callee_decl->getQualifiedNameAsString()))
return true;
m.to = method_call_expr->getMethodDecl()
->getParent()
->getQualifiedNameAsString();
m.to_usr = method_call_expr->getMethodDecl()->getID();
m.message = method_call_expr->getMethodDecl()->getNameAsString();
m.return_type = method_call_expr->getCallReturnType(current_ast_context)
.getAsString();
}
else if (const auto *function_call_expr =
clang::dyn_cast_or_null<clang::CallExpr>(expr);
function_call_expr != nullptr) {
assert(function_call_expr->getCalleeDecl()->getAsFunction());
m.to = function_call_expr->getCalleeDecl()
->getAsFunction()
->getQualifiedNameAsString() +
"()";
m.message = function_call_expr->getCalleeDecl()
->getAsFunction()
->getNameAsString();
m.to_usr =
function_call_expr->getCalleeDecl()->getAsFunction()->getID();
m.return_type =
function_call_expr->getCallReturnType(current_ast_context)
.getAsString();
}
else {
return true;
}
if (diagram().sequences.find(m.from_usr) == diagram().sequences.end()) {
activity a;
a.usr = m.from_usr;
a.from = m.from;
diagram().sequences.insert({m.from_usr, std::move(a)});
}
LOG_DBG("Found call {} from {} [{}] to {} [{}] ", m.message, m.from,
m.from_usr, m.to, m.to_usr);
diagram().sequences[m.from_usr].messages.emplace_back(std::move(m));
assert(!diagram().sequences.empty());
return true;
} }
*/
} }

View File

@@ -20,6 +20,7 @@
#include "config/config.h" #include "config/config.h"
#include "sequence_diagram/model/diagram.h" #include "sequence_diagram/model/diagram.h"
#include <clang/AST/Expr.h>
#include <clang/AST/RecursiveASTVisitor.h> #include <clang/AST/RecursiveASTVisitor.h>
#include <clang/Basic/SourceManager.h> #include <clang/Basic/SourceManager.h>
@@ -32,6 +33,18 @@ public:
clanguml::sequence_diagram::model::diagram &diagram, clanguml::sequence_diagram::model::diagram &diagram,
const clanguml::config::sequence_diagram &config); const clanguml::config::sequence_diagram &config);
virtual bool VisitCallExpr(clang::CallExpr *expr);
virtual bool VisitCXXMethodDecl(clang::CXXMethodDecl *method);
virtual bool VisitCXXRecordDecl(clang::CXXRecordDecl *cls);
virtual bool VisitFunctionDecl(clang::FunctionDecl *function_declaration);
clanguml::sequence_diagram::model::diagram &diagram();
const clanguml::config::sequence_diagram &config() const;
void finalize() { } void finalize() { }
private: private:
@@ -42,6 +55,10 @@ private:
// Reference to class diagram config // Reference to class diagram config
const clanguml::config::sequence_diagram &config_; const clanguml::config::sequence_diagram &config_;
clang::CXXRecordDecl *current_class_decl_;
clang::CXXMethodDecl *current_method_decl_;
clang::FunctionDecl *current_function_decl_;
}; };
} }

View File

@@ -16,7 +16,6 @@ set(CLANG_UML_TEST_LIBRARIES
clang-umllib clang-umllib
${YAML_CPP_LIBRARIES} ${YAML_CPP_LIBRARIES}
${LIBTOOLING_LIBS} ${LIBTOOLING_LIBS}
# ${LIBCLANG_LIBRARIES}
Threads::Threads) Threads::Threads)

View File

@@ -24,7 +24,7 @@ TEST_CASE("t20001", "[test-case][sequence]")
REQUIRE(diagram->name == "t20001_sequence"); REQUIRE(diagram->name == "t20001_sequence");
auto model = generate_sequence_diagram(db, diagram); auto model = generate_sequence_diagram(*db, diagram);
REQUIRE(model->name() == "t20001_sequence"); REQUIRE(model->name() == "t20001_sequence");

View File

@@ -24,7 +24,7 @@ TEST_CASE("t20002", "[test-case][sequence]")
REQUIRE(diagram->name == "t20002_sequence"); REQUIRE(diagram->name == "t20002_sequence");
auto model = generate_sequence_diagram(db, diagram); auto model = generate_sequence_diagram(*db, diagram);
REQUIRE(model->name() == "t20002_sequence"); REQUIRE(model->name() == "t20002_sequence");

View File

@@ -239,8 +239,8 @@ using namespace clanguml::test::matchers;
//// ////
//// Sequence diagram tests //// Sequence diagram tests
//// ////
//#include "t20001/test_case.h" #include "t20001/test_case.h"
//#include "t20002/test_case.h" #include "t20002/test_case.h"
// //
//// ////
//// Package diagram tests //// Package diagram tests