diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ee198c0..6ec9c838 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -154,7 +154,6 @@ add_library(clang-umllib OBJECT ${SOURCES}) # add_executable(clang-uml ${MAIN_SOURCE_FILE}) target_link_libraries(clang-uml -# ${LIBCLANG_LIBRARIES} ${YAML_CPP_LIBRARIES} ${LIBTOOLING_LIBS} clang-umllib diff --git a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc index a90b1983..972000e2 100644 --- a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc +++ b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc @@ -44,6 +44,13 @@ void generator::generate_call(const message &m, std::ostream &ostr) const ostr << '"' << from << "\" " << common::generators::plantuml::to_plantuml(message_t::kCall) << " \"" << 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 @@ -81,7 +88,7 @@ void generator::generate(std::ostream &ostr) const for (const auto &sf : m_config.start_from()) { 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) { if (v.from == sf.location) { start_from = k; diff --git a/src/sequence_diagram/model/diagram.h b/src/sequence_diagram/model/diagram.h index 23c00b96..808e0899 100644 --- a/src/sequence_diagram/model/diagram.h +++ b/src/sequence_diagram/model/diagram.h @@ -47,7 +47,7 @@ public: bool started{false}; - std::map sequences; + std::map sequences; }; } diff --git a/src/sequence_diagram/model/message.h b/src/sequence_diagram/model/message.h index cbe7cbfd..bb0b3212 100644 --- a/src/sequence_diagram/model/message.h +++ b/src/sequence_diagram/model/message.h @@ -29,7 +29,7 @@ struct message { std::string from; std::uint_least64_t from_usr; std::string to; - std::uint_least64_t to_usr; + std::int64_t to_usr; std::string message; std::string return_type; unsigned int line; diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index 76f99b05..051db978 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -29,106 +29,156 @@ translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm, : source_manager_{sm} , diagram_{diagram} , config_{config} + , current_class_decl_{nullptr} + , current_method_decl_{nullptr} + , current_function_decl_{nullptr} { } -/* -void translation_unit_visitor::process_activities(const cppast::cpp_function &e) +clanguml::sequence_diagram::model::diagram &translation_unit_visitor::diagram() { + return diagram_; +} - for (const auto &function_call_ptr : e.function_calls()) { - const auto &function_call = - static_cast(*function_call_ptr); +const clanguml::config::sequence_diagram & +translation_unit_visitor::config() const +{ + return config_; +} - message m; - m.type = message_t::kCall; +bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls) +{ + current_class_decl_ = cls; - if (!ctx.entity_index() - .lookup_definition(function_call.get_caller_id()) - .has_value()) - continue; + return true; +} - if (!ctx.entity_index() - .lookup_definition(function_call.get_caller_method_id()) - .has_value()) - continue; +bool translation_unit_visitor::VisitCXXMethodDecl(clang::CXXMethodDecl *method) +{ + current_method_decl_ = method; - if (!ctx.entity_index() - .lookup_definition(function_call.get_callee_id()) - .has_value()) - continue; + return true; +} - if (!ctx.entity_index() - .lookup_definition(function_call.get_callee_method_id()) - .has_value()) - continue; +bool translation_unit_visitor::VisitFunctionDecl( + clang::FunctionDecl *function_declaration) +{ + if (!function_declaration->isCXXClassMember()) + current_class_decl_ = nullptr; - const auto &caller = - ctx.entity_index() - .lookup_definition(function_call.get_caller_id()) - .value(); - m.from = cx::util::ns(caller) + "::" + caller.name(); + current_function_decl_ = function_declaration; - if (!ctx.diagram().should_include( - common::model::namespace_{cx::util::ns(caller)}, caller.name())) - continue; + return true; +} - if (caller.kind() == cpp_entity_kind::function_t) - m.from += "()"; +bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr) +{ + 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 = - ctx.entity_index() - .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 (expr->isImplicitCXXThis()) + return true; - if (!ctx.diagram().should_include( - common::model::namespace_{cx::util::ns(callee)}, callee.name())) - continue; + if (/*clang::dyn_cast_or_null(expr) ||*/ + clang::dyn_cast_or_null(expr)) + 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 = - static_cast( - ctx.entity_index() - .lookup_definition(function_call.get_callee_method_id()) - .value()); + if (current_function_decl_ && + !diagram().should_include( + current_function_decl_->getQualifiedNameAsString())) + return true; - m.message = callee_method.name(); + message m; + m.type = message_t::kCall; - m.return_type = cppast::to_string(callee_method.return_type()); - - if (ctx.diagram().sequences.find(m.from_usr) == - ctx.diagram().sequences.end()) { - activity a; - a.usr = m.from_usr; - a.from = m.from; - ctx.diagram().sequences.insert({m.from_usr, std::move(a)}); - } - - LOG_DBG("Adding sequence {} -{}()-> {}", m.from, m.message, m.to); - - ctx.diagram().sequences[m.from_usr].messages.emplace_back(std::move(m)); + if (current_class_decl_ != nullptr) { + assert(current_method_decl_ != nullptr); + m.from = current_class_decl_->getQualifiedNameAsString(); + m.from_usr = current_method_decl_->getID(); + } + else { + m.from = current_function_decl_->getQualifiedNameAsString() + "()"; + m.from_usr = current_function_decl_->getID(); } -} -void translation_unit_visitor::operator()(const cppast::cpp_entity &file) -{ + const auto ¤t_ast_context = current_class_decl_ + ? current_class_decl_->getASTContext() + : current_function_decl_->getASTContext(); - cppast::visit(file, [&, this](const cpp_entity &e, visitor_info) { - if (e.kind() == cpp_entity_kind::function_t) { - const auto &function = static_cast(e); - process_activities(function); - } - else if (e.kind() == cpp_entity_kind::member_function_t) { - const auto &member_function = static_cast(e); - process_activities(member_function); - } - }); + if (const auto *operator_call_expr = + clang::dyn_cast_or_null(expr); + operator_call_expr != nullptr) { + [[maybe_unused]] const auto *callee_method_decl = + operator_call_expr->getCalleeDecl(); + } + else if (const auto *method_call_expr = + clang::dyn_cast_or_null(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(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; } -*/ } diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.h b/src/sequence_diagram/visitor/translation_unit_visitor.h index b8d8107f..a5e3fbf5 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.h +++ b/src/sequence_diagram/visitor/translation_unit_visitor.h @@ -20,6 +20,7 @@ #include "config/config.h" #include "sequence_diagram/model/diagram.h" +#include #include #include @@ -32,6 +33,18 @@ public: clanguml::sequence_diagram::model::diagram &diagram, 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() { } private: @@ -42,6 +55,10 @@ private: // Reference to class diagram config const clanguml::config::sequence_diagram &config_; + + clang::CXXRecordDecl *current_class_decl_; + clang::CXXMethodDecl *current_method_decl_; + clang::FunctionDecl *current_function_decl_; }; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 350c7a12..7de8841c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,7 +16,6 @@ set(CLANG_UML_TEST_LIBRARIES clang-umllib ${YAML_CPP_LIBRARIES} ${LIBTOOLING_LIBS} -# ${LIBCLANG_LIBRARIES} Threads::Threads) diff --git a/tests/t20001/test_case.h b/tests/t20001/test_case.h index d330bfd9..82e44243 100644 --- a/tests/t20001/test_case.h +++ b/tests/t20001/test_case.h @@ -24,7 +24,7 @@ TEST_CASE("t20001", "[test-case][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"); diff --git a/tests/t20002/test_case.h b/tests/t20002/test_case.h index a5ab0472..ebefda0f 100644 --- a/tests/t20002/test_case.h +++ b/tests/t20002/test_case.h @@ -24,7 +24,7 @@ TEST_CASE("t20002", "[test-case][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"); diff --git a/tests/test_cases.cc b/tests/test_cases.cc index 8e5fe618..00a51dfe 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -239,8 +239,8 @@ using namespace clanguml::test::matchers; //// //// Sequence diagram tests //// -//#include "t20001/test_case.h" -//#include "t20002/test_case.h" +#include "t20001/test_case.h" +#include "t20002/test_case.h" // //// //// Package diagram tests