Refactored sequence diagram generator to cppast
This commit is contained in:
@@ -151,21 +151,35 @@ template <> struct convert<scope_t> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <> struct convert<std::vector<source_location::variant>> {
|
template <> struct convert<std::vector<source_location>> {
|
||||||
static bool decode(
|
static bool decode(const Node &node, std::vector<source_location> &rhs)
|
||||||
const Node &node, std::vector<source_location::variant> &rhs)
|
|
||||||
{
|
{
|
||||||
for (auto it = node.begin(); it != node.end(); ++it) {
|
for (auto it = node.begin(); it != node.end(); ++it) {
|
||||||
const YAML::Node &n = *it;
|
const YAML::Node &n = *it;
|
||||||
if (n["usr"]) {
|
if (n["usr"]) {
|
||||||
rhs.emplace_back(n["usr"].as<source_location::usr>());
|
source_location loc;
|
||||||
|
loc.location_type = source_location::location_t::usr;
|
||||||
|
loc.location = n["usr"].as<std::string>();
|
||||||
|
rhs.emplace_back(std::move(loc));
|
||||||
}
|
}
|
||||||
else if (n["marker"]) {
|
else if (n["marker"]) {
|
||||||
rhs.emplace_back(n["marker"].as<source_location::marker>());
|
source_location loc;
|
||||||
|
loc.location_type = source_location::location_t::marker;
|
||||||
|
loc.location = n["marker"].as<std::string>();
|
||||||
|
rhs.emplace_back(std::move(loc));
|
||||||
}
|
}
|
||||||
else if (n["file"] && n["line"]) {
|
else if (n["file"] && n["line"]) {
|
||||||
rhs.emplace_back(std::make_pair(
|
source_location loc;
|
||||||
n["file"].as<std::string>(), n["line"].as<int>()));
|
loc.location_type = source_location::location_t::fileline;
|
||||||
|
loc.location = n["file"].as<std::string>() + ":" +
|
||||||
|
n["line"].as<std::string>();
|
||||||
|
rhs.emplace_back(std::move(loc));
|
||||||
|
}
|
||||||
|
else if (n["function"]) {
|
||||||
|
source_location loc;
|
||||||
|
loc.location_type = source_location::location_t::function;
|
||||||
|
loc.location = n["function"].as<std::string>();
|
||||||
|
rhs.emplace_back(std::move(loc));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return false;
|
return false;
|
||||||
@@ -261,7 +275,7 @@ template <> struct convert<sequence_diagram> {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (node["start_from"])
|
if (node["start_from"])
|
||||||
rhs.start_from = node["start_from"].as<decltype(rhs.start_from)>();
|
rhs.start_from = node["start_from"].as<std::vector<source_location>>();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,13 +81,9 @@ struct diagram {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct source_location {
|
struct source_location {
|
||||||
using usr = std::string;
|
enum class location_t { usr, marker, fileline, function };
|
||||||
using marker = std::string;
|
location_t location_type;
|
||||||
using file = std::pair<std::string, int>;
|
std::string location;
|
||||||
// std::variant requires unique types, so we cannot add
|
|
||||||
// marker here, we need sth like boost::mp_unique
|
|
||||||
// type function
|
|
||||||
using variant = std::variant<usr, /* marker, */ file>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct class_diagram : public diagram {
|
struct class_diagram : public diagram {
|
||||||
@@ -102,7 +98,7 @@ struct class_diagram : public diagram {
|
|||||||
struct sequence_diagram : public diagram {
|
struct sequence_diagram : public diagram {
|
||||||
virtual ~sequence_diagram() = default;
|
virtual ~sequence_diagram() = default;
|
||||||
|
|
||||||
std::vector<source_location::variant> start_from;
|
std::vector<source_location> start_from;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct config {
|
struct config {
|
||||||
|
|||||||
@@ -77,10 +77,7 @@ int main(int argc, const char *argv[])
|
|||||||
LOG_INFO("Loading compilation database from {} directory",
|
LOG_INFO("Loading compilation database from {} directory",
|
||||||
config.compilation_database_dir);
|
config.compilation_database_dir);
|
||||||
|
|
||||||
auto db =
|
cppast::libclang_compilation_database db(config.compilation_database_dir);
|
||||||
compilation_database::from_directory(config.compilation_database_dir);
|
|
||||||
|
|
||||||
cppast::libclang_compilation_database db2(config.compilation_database_dir);
|
|
||||||
|
|
||||||
for (const auto &[name, diagram] : config.diagrams) {
|
for (const auto &[name, diagram] : config.diagrams) {
|
||||||
// If there are any specific diagram names provided on the command line,
|
// If there are any specific diagram names provided on the command line,
|
||||||
@@ -100,7 +97,7 @@ int main(int argc, const char *argv[])
|
|||||||
if (std::dynamic_pointer_cast<class_diagram>(diagram)) {
|
if (std::dynamic_pointer_cast<class_diagram>(diagram)) {
|
||||||
auto model =
|
auto model =
|
||||||
clanguml::class_diagram::generators::plantuml::generate(
|
clanguml::class_diagram::generators::plantuml::generate(
|
||||||
db2, name, dynamic_cast<class_diagram &>(*diagram));
|
db, name, dynamic_cast<class_diagram &>(*diagram));
|
||||||
|
|
||||||
ofs << clanguml::class_diagram::generators::plantuml::generator(
|
ofs << clanguml::class_diagram::generators::plantuml::generator(
|
||||||
dynamic_cast<clanguml::config::class_diagram &>(*diagram),
|
dynamic_cast<clanguml::config::class_diagram &>(*diagram),
|
||||||
|
|||||||
@@ -20,6 +20,9 @@
|
|||||||
|
|
||||||
#include "sequence_diagram/visitor/translation_unit_context.h"
|
#include "sequence_diagram/visitor/translation_unit_context.h"
|
||||||
|
|
||||||
|
#include <cppast/libclang_parser.hpp>
|
||||||
|
#include <cppast/parser.hpp>
|
||||||
|
|
||||||
namespace clanguml::sequence_diagram::generators::plantuml {
|
namespace clanguml::sequence_diagram::generators::plantuml {
|
||||||
|
|
||||||
using diagram_model = clanguml::sequence_diagram::model::diagram;
|
using diagram_model = clanguml::sequence_diagram::model::diagram;
|
||||||
@@ -97,15 +100,20 @@ void generator::generate(std::ostream &ostr) const
|
|||||||
ostr << b << std::endl;
|
ostr << b << std::endl;
|
||||||
|
|
||||||
for (const auto &sf : m_config.start_from) {
|
for (const auto &sf : m_config.start_from) {
|
||||||
std::string start_from;
|
if (sf.location_type == source_location::location_t::function) {
|
||||||
if (std::holds_alternative<source_location::usr>(sf)) {
|
std::uint_least64_t start_from;
|
||||||
start_from = std::get<source_location::usr>(sf);
|
for (const auto &[k, v] : m_model.sequences) {
|
||||||
|
if (v.from == sf.location) {
|
||||||
|
start_from = k;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
generate_activity(m_model.sequences[start_from], ostr);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// TODO: Add support for other sequence start location types
|
// TODO: Add support for other sequence start location types
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
generate_activity(m_model.sequences[start_from], ostr);
|
|
||||||
}
|
}
|
||||||
for (const auto &a : m_config.puml.after)
|
for (const auto &a : m_config.puml.after)
|
||||||
ostr << a << std::endl;
|
ostr << a << std::endl;
|
||||||
@@ -120,51 +128,33 @@ std::ostream &operator<<(std::ostream &os, const generator &g)
|
|||||||
}
|
}
|
||||||
|
|
||||||
clanguml::sequence_diagram::model::diagram generate(
|
clanguml::sequence_diagram::model::diagram generate(
|
||||||
clanguml::cx::compilation_database &db, const std::string &name,
|
cppast::libclang_compilation_database &db, const std::string &name,
|
||||||
clanguml::config::sequence_diagram &diagram)
|
clanguml::config::sequence_diagram &diagram)
|
||||||
{
|
{
|
||||||
spdlog::info("Generating diagram {}.puml", name);
|
spdlog::info("Generating diagram {}.puml", name);
|
||||||
clanguml::sequence_diagram::model::diagram d;
|
clanguml::sequence_diagram::model::diagram d;
|
||||||
d.name = name;
|
d.name = name;
|
||||||
|
|
||||||
|
cppast::cpp_entity_index idx;
|
||||||
|
cppast::simple_file_parser<cppast::libclang_parser> parser{
|
||||||
|
type_safe::ref(idx)};
|
||||||
|
|
||||||
|
clanguml::sequence_diagram::visitor::translation_unit_visitor visitor(
|
||||||
|
idx, d, diagram);
|
||||||
|
|
||||||
// Get all translation units matching the glob from diagram
|
// Get all translation units matching the glob from diagram
|
||||||
// configuration
|
// configuration
|
||||||
std::vector<std::filesystem::path> translation_units{};
|
std::vector<std::string> translation_units{};
|
||||||
for (const auto &g : diagram.glob) {
|
for (const auto &g : diagram.glob) {
|
||||||
spdlog::debug("Processing glob: {}", g);
|
spdlog::debug("Processing glob: {}", g);
|
||||||
const auto matches = glob::rglob(g);
|
const auto matches = glob::rglob(g);
|
||||||
std::copy(matches.begin(), matches.end(),
|
std::copy(matches.begin(), matches.end(),
|
||||||
std::back_inserter(translation_units));
|
std::back_inserter(translation_units));
|
||||||
}
|
}
|
||||||
|
cppast::parse_files(parser, translation_units, db);
|
||||||
|
|
||||||
// Process all matching translation units
|
for (auto &file : parser.files())
|
||||||
for (const auto &tu_path : translation_units) {
|
visitor(file);
|
||||||
spdlog::debug("Processing translation unit: {}",
|
|
||||||
std::filesystem::canonical(tu_path).c_str());
|
|
||||||
|
|
||||||
auto tu = db.parse_translation_unit(tu_path);
|
|
||||||
|
|
||||||
auto cursor = clang_getTranslationUnitCursor(tu);
|
|
||||||
|
|
||||||
if (clang_Cursor_isNull(cursor)) {
|
|
||||||
spdlog::debug("Cursor is NULL");
|
|
||||||
}
|
|
||||||
|
|
||||||
spdlog::debug("Cursor kind: {}",
|
|
||||||
clang_getCString(clang_getCursorKindSpelling(cursor.kind)));
|
|
||||||
spdlog::debug("Cursor name: {}",
|
|
||||||
clang_getCString(clang_getCursorDisplayName(cursor)));
|
|
||||||
|
|
||||||
clanguml::sequence_diagram::visitor::translation_unit_context ctx(
|
|
||||||
d, diagram);
|
|
||||||
auto res = clang_visitChildren(cursor,
|
|
||||||
clanguml::sequence_diagram::visitor::translation_unit_visitor,
|
|
||||||
&ctx);
|
|
||||||
|
|
||||||
spdlog::debug("Processing result: {}", res);
|
|
||||||
|
|
||||||
clang_suspendTranslationUnit(tu);
|
|
||||||
}
|
|
||||||
|
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include "util/util.h"
|
#include "util/util.h"
|
||||||
|
|
||||||
#include <glob/glob.hpp>
|
#include <glob/glob.hpp>
|
||||||
|
#include <cppast/libclang_parser.hpp>
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -62,7 +63,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
clanguml::sequence_diagram::model::diagram generate(
|
clanguml::sequence_diagram::model::diagram generate(
|
||||||
clanguml::cx::compilation_database &db, const std::string &name,
|
cppast::libclang_compilation_database &db, const std::string &name,
|
||||||
clanguml::config::sequence_diagram &diagram);
|
clanguml::config::sequence_diagram &diagram);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
namespace clanguml::sequence_diagram::model {
|
namespace clanguml::sequence_diagram::model {
|
||||||
|
|
||||||
struct activity {
|
struct activity {
|
||||||
std::string usr;
|
std::uint_least64_t usr;
|
||||||
std::string from;
|
std::string from;
|
||||||
std::vector<message> messages;
|
std::vector<message> messages;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ struct diagram {
|
|||||||
bool started{false};
|
bool started{false};
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
std::map<std::string, activity> sequences;
|
std::map<std::uint_least64_t, activity> sequences;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ namespace clanguml::sequence_diagram::model {
|
|||||||
struct message {
|
struct message {
|
||||||
message_t type;
|
message_t type;
|
||||||
std::string from;
|
std::string from;
|
||||||
std::string from_usr;
|
std::uint_least64_t from_usr;
|
||||||
std::string to;
|
std::string to;
|
||||||
std::string to_usr;
|
std::uint_least64_t to_usr;
|
||||||
std::string message;
|
std::string message;
|
||||||
std::string return_type;
|
std::string return_type;
|
||||||
unsigned int line;
|
unsigned int line;
|
||||||
|
|||||||
@@ -25,9 +25,11 @@
|
|||||||
namespace clanguml::sequence_diagram::visitor {
|
namespace clanguml::sequence_diagram::visitor {
|
||||||
|
|
||||||
translation_unit_context::translation_unit_context(
|
translation_unit_context::translation_unit_context(
|
||||||
|
cppast::cpp_entity_index &idx,
|
||||||
clanguml::sequence_diagram::model::diagram &diagram,
|
clanguml::sequence_diagram::model::diagram &diagram,
|
||||||
const clanguml::config::sequence_diagram &config)
|
const clanguml::config::sequence_diagram &config)
|
||||||
: diagram_{diagram}
|
: entity_index_{idx}
|
||||||
|
, diagram_{diagram}
|
||||||
, config_{config}
|
, config_{config}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -55,6 +57,11 @@ clanguml::sequence_diagram::model::diagram &translation_unit_context::diagram()
|
|||||||
return diagram_;
|
return diagram_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cppast::cpp_entity_index &translation_unit_context::entity_index() const
|
||||||
|
{
|
||||||
|
return entity_index_;
|
||||||
|
}
|
||||||
|
|
||||||
void translation_unit_context::set_current_method(cx::cursor method)
|
void translation_unit_context::set_current_method(cx::cursor method)
|
||||||
{
|
{
|
||||||
current_method_ = method;
|
current_method_ = method;
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
#include "cx/cursor.h"
|
#include "cx/cursor.h"
|
||||||
#include "sequence_diagram/model/diagram.h"
|
#include "sequence_diagram/model/diagram.h"
|
||||||
|
|
||||||
|
#include <cppast/cpp_entity_index.hpp>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -29,7 +31,7 @@ namespace clanguml::sequence_diagram::visitor {
|
|||||||
|
|
||||||
class translation_unit_context {
|
class translation_unit_context {
|
||||||
public:
|
public:
|
||||||
translation_unit_context(
|
translation_unit_context(cppast::cpp_entity_index &idx,
|
||||||
clanguml::sequence_diagram::model::diagram &diagram,
|
clanguml::sequence_diagram::model::diagram &diagram,
|
||||||
const clanguml::config::sequence_diagram &config);
|
const clanguml::config::sequence_diagram &config);
|
||||||
|
|
||||||
@@ -50,10 +52,13 @@ public:
|
|||||||
cx::cursor ¤t_method();
|
cx::cursor ¤t_method();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::string> namespace_;
|
// Reference to the cppast entity index
|
||||||
cx::cursor current_method_;
|
cppast::cpp_entity_index &entity_index_;
|
||||||
clanguml::sequence_diagram::model::diagram &diagram_;
|
clanguml::sequence_diagram::model::diagram &diagram_;
|
||||||
const clanguml::config::sequence_diagram &config_;
|
const clanguml::config::sequence_diagram &config_;
|
||||||
|
|
||||||
|
std::vector<std::string> namespace_;
|
||||||
|
cx::cursor current_method_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,124 +20,125 @@
|
|||||||
|
|
||||||
#include "translation_unit_context.h"
|
#include "translation_unit_context.h"
|
||||||
|
|
||||||
|
#include <cppast/cpp_function.hpp>
|
||||||
|
#include <cppast/cpp_member_function.hpp>
|
||||||
|
#include <cppast/visitor.hpp>
|
||||||
|
|
||||||
namespace clanguml::sequence_diagram::visitor {
|
namespace clanguml::sequence_diagram::visitor {
|
||||||
|
|
||||||
enum CXChildVisitResult translation_unit_visitor(
|
translation_unit_visitor::translation_unit_visitor(
|
||||||
CXCursor cx_cursor, CXCursor cx_parent, CXClientData client_data)
|
cppast::cpp_entity_index &idx,
|
||||||
|
clanguml::sequence_diagram::model::diagram &diagram,
|
||||||
|
const clanguml::config::sequence_diagram &config)
|
||||||
|
: ctx{idx, diagram, config}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void translation_unit_visitor::process_activities(const cppast::cpp_function &e)
|
||||||
{
|
{
|
||||||
using clanguml::sequence_diagram::model::activity;
|
using clanguml::sequence_diagram::model::activity;
|
||||||
using clanguml::sequence_diagram::model::diagram;
|
using clanguml::sequence_diagram::model::diagram;
|
||||||
using clanguml::sequence_diagram::model::message;
|
using clanguml::sequence_diagram::model::message;
|
||||||
using clanguml::sequence_diagram::model::message_t;
|
using clanguml::sequence_diagram::model::message_t;
|
||||||
|
using cppast::cpp_entity;
|
||||||
|
using cppast::cpp_entity_kind;
|
||||||
|
using cppast::cpp_function;
|
||||||
|
using cppast::cpp_member_function;
|
||||||
|
using cppast::cpp_member_function_call;
|
||||||
|
using cppast::visitor_info;
|
||||||
|
|
||||||
auto *ctx = (struct translation_unit_context *)client_data;
|
for (const auto &function_call_ptr : e.function_calls()) {
|
||||||
|
const auto &function_call =
|
||||||
|
static_cast<const cpp_member_function_call &>(*function_call_ptr);
|
||||||
|
|
||||||
enum CXChildVisitResult ret = CXChildVisit_Break;
|
message m;
|
||||||
|
m.type = message_t::kCall;
|
||||||
|
|
||||||
cx::cursor cursor{std::move(cx_cursor)};
|
if (!ctx.entity_index()
|
||||||
cx::cursor parent{std::move(cx_parent)};
|
.lookup_definition(function_call.get_caller_id())
|
||||||
|
.has_value())
|
||||||
|
continue;
|
||||||
|
|
||||||
if (cursor.spelling().empty()) {
|
if (!ctx.entity_index()
|
||||||
return CXChildVisit_Recurse;
|
.lookup_definition(function_call.get_caller_method_id())
|
||||||
}
|
.has_value())
|
||||||
|
continue;
|
||||||
|
|
||||||
switch (cursor.kind()) {
|
if (!ctx.entity_index()
|
||||||
case CXCursor_FunctionTemplate:
|
.lookup_definition(function_call.get_callee_id())
|
||||||
case CXCursor_CXXMethod:
|
.has_value())
|
||||||
case CXCursor_FunctionDecl:
|
continue;
|
||||||
ctx->set_current_method(cursor);
|
|
||||||
ret = CXChildVisit_Recurse;
|
|
||||||
break;
|
|
||||||
case CXCursor_CallExpr: {
|
|
||||||
auto referenced = cursor.referenced();
|
|
||||||
auto referenced_type = referenced.type();
|
|
||||||
auto referenced_cursor_name = referenced.display_name();
|
|
||||||
|
|
||||||
auto semantic_parent = referenced.semantic_parent();
|
if (!ctx.entity_index()
|
||||||
auto sp_name = semantic_parent.fully_qualified();
|
.lookup_definition(function_call.get_callee_method_id())
|
||||||
auto lexical_parent = cursor.lexical_parent();
|
.has_value())
|
||||||
auto lp_name = lexical_parent.spelling();
|
continue;
|
||||||
|
|
||||||
CXFile f;
|
const auto &caller =
|
||||||
unsigned int line{};
|
ctx.entity_index()
|
||||||
unsigned int column{};
|
.lookup_definition(function_call.get_caller_id())
|
||||||
unsigned int offset{};
|
.value();
|
||||||
clang_getFileLocation(cursor.location(), &f, &line, &column, &offset);
|
m.from = cx::util::ns(caller) + "::" + caller.name();
|
||||||
std::string file{clang_getCString(clang_getFileName(f))};
|
|
||||||
|
|
||||||
auto &d = ctx->diagram();
|
if (!ctx.config().should_include(m.from))
|
||||||
auto &config = ctx->config();
|
continue;
|
||||||
if (referenced.kind() == CXCursor_CXXMethod) {
|
|
||||||
if (config.should_include(sp_name)) {
|
|
||||||
// Get calling object
|
|
||||||
std::string caller{};
|
|
||||||
if (ctx->current_method()
|
|
||||||
.semantic_parent()
|
|
||||||
.is_translation_unit() ||
|
|
||||||
ctx->current_method().semantic_parent().is_namespace()) {
|
|
||||||
caller = ctx->current_method()
|
|
||||||
.semantic_parent()
|
|
||||||
.fully_qualified() +
|
|
||||||
"::" + ctx->current_method().spelling() + "()";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
caller = ctx->current_method()
|
|
||||||
.semantic_parent()
|
|
||||||
.fully_qualified();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto caller_usr = ctx->current_method().usr();
|
if (caller.kind() == cpp_entity_kind::function_t)
|
||||||
// Get called object
|
m.from += "()";
|
||||||
auto callee = referenced.semantic_parent().fully_qualified();
|
|
||||||
auto callee_usr = referenced.semantic_parent().usr();
|
|
||||||
|
|
||||||
// Get called method
|
|
||||||
auto called_message = cursor.spelling();
|
|
||||||
|
|
||||||
// Found method call: CXCursorKind () const
|
m.from_usr = type_safe::get(function_call.get_caller_method_id());
|
||||||
spdlog::debug("Adding method call at line {}:{} to diagram {}"
|
|
||||||
"\n\tCURRENT_METHOD: {}\n\tFROM: '{}'\n\tTO: "
|
|
||||||
"{}\n\tMESSAGE: {}\n\tFROM_USR: {}\n\tTO_USR: "
|
|
||||||
"{}\n\tRETURN_TYPE: {}",
|
|
||||||
file, line, d.name, ctx->current_method().spelling(),
|
|
||||||
caller, callee, called_message, caller_usr, callee_usr,
|
|
||||||
referenced.type().result_type().spelling());
|
|
||||||
|
|
||||||
message m;
|
const auto &callee =
|
||||||
m.type = message_t::kCall;
|
ctx.entity_index()
|
||||||
m.from = caller;
|
.lookup_definition(function_call.get_callee_id())
|
||||||
m.from_usr = caller_usr;
|
.value();
|
||||||
m.line = line;
|
m.to = cx::util::ns(callee) + "::" + callee.name();
|
||||||
m.to = callee;
|
|
||||||
m.to_usr = referenced.usr();
|
|
||||||
m.message = called_message;
|
|
||||||
m.return_type = referenced.type().result_type().spelling();
|
|
||||||
|
|
||||||
if (d.sequences.find(caller_usr) == d.sequences.end()) {
|
if (!ctx.config().should_include(m.to))
|
||||||
activity a;
|
continue;
|
||||||
a.usr = caller_usr;
|
|
||||||
a.from = caller;
|
|
||||||
d.sequences.insert({caller_usr, std::move(a)});
|
|
||||||
}
|
|
||||||
|
|
||||||
d.sequences[caller_usr].messages.emplace_back(std::move(m));
|
m.to_usr = type_safe::get(function_call.get_callee_method_id());
|
||||||
}
|
|
||||||
}
|
const auto &callee_method =
|
||||||
else if (referenced.kind() == CXCursor_FunctionDecl) {
|
ctx.entity_index()
|
||||||
// TODO
|
.lookup_definition(function_call.get_callee_method_id())
|
||||||
|
.value();
|
||||||
|
|
||||||
|
m.message = callee_method.name();
|
||||||
|
|
||||||
|
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)});
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = CXChildVisit_Recurse;
|
LOG_DBG("Adding sequence {} -{}()-> {}", m.from, m.message, m.to);
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CXCursor_Namespace: {
|
|
||||||
ret = CXChildVisit_Recurse;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
ret = CXChildVisit_Recurse;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
ctx.diagram().sequences[m.from_usr].messages.emplace_back(std::move(m));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
|
||||||
|
{
|
||||||
|
using cppast::cpp_entity;
|
||||||
|
using cppast::cpp_entity_kind;
|
||||||
|
using cppast::cpp_function;
|
||||||
|
using cppast::cpp_member_function;
|
||||||
|
using cppast::cpp_member_function_call;
|
||||||
|
using cppast::visitor_info;
|
||||||
|
|
||||||
|
cppast::visit(file, [&, this](const cpp_entity &e, visitor_info info) {
|
||||||
|
if (e.kind() == cpp_entity_kind::function_t) {
|
||||||
|
const auto &function = static_cast<const cpp_function &>(e);
|
||||||
|
process_activities(function);
|
||||||
|
}
|
||||||
|
else if (e.kind() == cpp_entity_kind::member_function_t) {
|
||||||
|
const auto &member_function = static_cast<const cpp_function &>(e);
|
||||||
|
process_activities(member_function);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,10 +20,24 @@
|
|||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "cx/cursor.h"
|
#include "cx/cursor.h"
|
||||||
#include "sequence_diagram/model/diagram.h"
|
#include "sequence_diagram/model/diagram.h"
|
||||||
|
#include "sequence_diagram/visitor/translation_unit_context.h"
|
||||||
|
|
||||||
|
#include <cppast/cpp_function.hpp>
|
||||||
|
|
||||||
namespace clanguml::sequence_diagram::visitor {
|
namespace clanguml::sequence_diagram::visitor {
|
||||||
|
|
||||||
enum CXChildVisitResult translation_unit_visitor(
|
class translation_unit_visitor {
|
||||||
CXCursor cx_cursor, CXCursor cx_parent, CXClientData client_data);
|
public:
|
||||||
|
translation_unit_visitor(cppast::cpp_entity_index &idx,
|
||||||
|
clanguml::sequence_diagram::model::diagram &diagram,
|
||||||
|
const clanguml::config::sequence_diagram &config);
|
||||||
|
|
||||||
|
void operator()(const cppast::cpp_entity &file);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void process_activities(const cppast::cpp_function &e);
|
||||||
|
|
||||||
|
// ctx allows to track current visitor context, e.g. current namespace
|
||||||
|
translation_unit_context ctx;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ diagrams:
|
|||||||
using_namespace:
|
using_namespace:
|
||||||
- clanguml::t20001
|
- clanguml::t20001
|
||||||
start_from:
|
start_from:
|
||||||
- usr: "c:@N@clanguml@N@t20001@F@tmain#"
|
- function: "clanguml::t20001::tmain()"
|
||||||
plantuml:
|
plantuml:
|
||||||
before:
|
before:
|
||||||
- "' t20001 test sequence diagram"
|
- "' t20001 test sequence diagram"
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
TEST_CASE("t20001", "[test-case][sequence]")
|
TEST_CASE("t20001", "[test-case][sequence]")
|
||||||
{
|
{
|
||||||
auto [config, db] = load_config2("t20001");
|
auto [config, db] = load_config("t20001");
|
||||||
|
|
||||||
auto diagram = config.diagrams["t20001_sequence"];
|
auto diagram = config.diagrams["t20001_sequence"];
|
||||||
|
|
||||||
@@ -49,6 +49,7 @@ TEST_CASE("t20001", "[test-case][sequence]")
|
|||||||
REQUIRE_THAT(puml, HasCall("B", "A", "log_result"));
|
REQUIRE_THAT(puml, HasCall("B", "A", "log_result"));
|
||||||
REQUIRE_THAT(puml, HasCallWithResponse("B", "A", "add3"));
|
REQUIRE_THAT(puml, HasCallWithResponse("B", "A", "add3"));
|
||||||
REQUIRE_THAT(puml, HasCall("A", "add"));
|
REQUIRE_THAT(puml, HasCall("A", "add"));
|
||||||
|
REQUIRE_THAT(puml, !HasCall("A", "detail::C", "add"));
|
||||||
|
|
||||||
save_puml(
|
save_puml(
|
||||||
"./" + config.output_directory + "/" + diagram->name + ".puml", puml);
|
"./" + config.output_directory + "/" + diagram->name + ".puml", puml);
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ std::pair<clanguml::config::config, compilation_database> load_config2(
|
|||||||
}
|
}
|
||||||
|
|
||||||
clanguml::sequence_diagram::model::diagram generate_sequence_diagram(
|
clanguml::sequence_diagram::model::diagram generate_sequence_diagram(
|
||||||
compilation_database &db,
|
cppast::libclang_compilation_database &db,
|
||||||
std::shared_ptr<clanguml::config::diagram> diagram)
|
std::shared_ptr<clanguml::config::diagram> diagram)
|
||||||
{
|
{
|
||||||
auto diagram_model =
|
auto diagram_model =
|
||||||
|
|||||||
Reference in New Issue
Block a user