Initial working sequence diagram
This commit is contained in:
@@ -5,9 +5,9 @@
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
namespace clanguml {
|
||||
namespace config {
|
||||
@@ -18,7 +18,7 @@ struct diagram {
|
||||
std::string name;
|
||||
std::vector<std::string> glob;
|
||||
std::vector<std::string> puml;
|
||||
std::string using_namespace;
|
||||
std::vector<std::string> using_namespace;
|
||||
};
|
||||
|
||||
enum class class_scopes { public_, protected_, private_ };
|
||||
@@ -34,12 +34,14 @@ struct class_diagram : public diagram {
|
||||
{
|
||||
spdlog::debug("CHECKING IF {} IS WHITE LISTED", clazz);
|
||||
for (const auto &c : classes) {
|
||||
std::string prefix{};
|
||||
if (!using_namespace.empty()) {
|
||||
prefix = using_namespace + "::";
|
||||
for (const auto &ns : using_namespace) {
|
||||
std::string prefix{};
|
||||
if (!ns.empty()) {
|
||||
prefix = ns + "::";
|
||||
}
|
||||
if (prefix + c == clazz)
|
||||
return true;
|
||||
}
|
||||
if (prefix + c == clazz)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -81,7 +83,8 @@ using clanguml::config::source_location;
|
||||
template <> struct convert<class_diagram> {
|
||||
static bool decode(const Node &node, class_diagram &rhs)
|
||||
{
|
||||
rhs.using_namespace = node["using_namespace"].as<std::string>();
|
||||
rhs.using_namespace =
|
||||
node["using_namespace"].as<std::vector<std::string>>();
|
||||
rhs.glob = node["glob"].as<std::vector<std::string>>();
|
||||
rhs.puml = node["puml"].as<std::vector<std::string>>();
|
||||
rhs.classes = node["classes"].as<std::vector<std::string>>();
|
||||
@@ -98,18 +101,18 @@ template <> struct convert<source_location> {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// sequence_diagram Yaml decoder
|
||||
//
|
||||
template <> struct convert<sequence_diagram> {
|
||||
static bool decode(const Node &node, sequence_diagram &rhs)
|
||||
{
|
||||
rhs.using_namespace = node["using_namespace"].as<std::string>();
|
||||
rhs.using_namespace =
|
||||
node["using_namespace"].as<std::vector<std::string>>();
|
||||
rhs.glob = node["glob"].as<std::vector<std::string>>();
|
||||
rhs.puml = node["puml"].as<std::vector<std::string>>();
|
||||
|
||||
if(node["start_from"])
|
||||
if (node["start_from"])
|
||||
rhs.start_from = node["start_from"].as<source_location>();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,12 @@ public:
|
||||
return to_string(clang_getCursorSpelling(m_cursor));
|
||||
}
|
||||
|
||||
bool is_void() const
|
||||
{
|
||||
// Why do I have to do this like this?
|
||||
return spelling() == "void";
|
||||
}
|
||||
|
||||
std::string fully_qualified() const
|
||||
{
|
||||
std::list<std::string> res;
|
||||
@@ -97,6 +103,8 @@ public:
|
||||
|
||||
bool is_statement() const { return clang_isStatement(kind()); }
|
||||
|
||||
bool is_namespace() const { return kind() == CXCursor_Namespace; }
|
||||
|
||||
bool is_attribute() const { return clang_isAttribute(kind()); }
|
||||
|
||||
bool has_attrs() const { return clang_Cursor_hasAttrs(m_cursor); }
|
||||
|
||||
21
src/main.cc
21
src/main.cc
@@ -1,9 +1,9 @@
|
||||
#include "config/config.h"
|
||||
#include "cx/compilation_database.h"
|
||||
#include "puml/class_diagram_generator.h"
|
||||
#include "puml/sequence_diagram_generator.h"
|
||||
#include "uml/class_diagram_model.h"
|
||||
#include "uml/class_diagram_visitor.h"
|
||||
#include "cx/compilation_database.h"
|
||||
#include "uml/sequence_diagram_visitor.h"
|
||||
|
||||
#include <cli11/CLI11.hpp>
|
||||
@@ -47,7 +47,7 @@ int main(int argc, const char *argv[])
|
||||
|
||||
spdlog::info("Loading clang-uml config from {}", config_path);
|
||||
|
||||
auto config = config::load(config_path);
|
||||
auto config = clanguml::config::load(config_path);
|
||||
|
||||
spdlog::info("Loading compilation database from {} directory",
|
||||
config.compilation_database_dir);
|
||||
@@ -56,8 +56,8 @@ int main(int argc, const char *argv[])
|
||||
compilation_database::from_directory(config.compilation_database_dir);
|
||||
|
||||
for (const auto &[name, diagram] : config.diagrams) {
|
||||
using config::class_diagram;
|
||||
using config::sequence_diagram;
|
||||
using clanguml::config::class_diagram;
|
||||
using clanguml::config::sequence_diagram;
|
||||
|
||||
if (std::dynamic_pointer_cast<class_diagram>(diagram)) {
|
||||
generators::class_diagram::generate(
|
||||
@@ -67,6 +67,19 @@ int main(int argc, const char *argv[])
|
||||
generators::sequence_diagram::generate(
|
||||
db, name, dynamic_cast<sequence_diagram &>(*diagram));
|
||||
}
|
||||
/*
|
||||
std::filesystem::path path{"puml/" + name + ".puml"};
|
||||
std::ofstream ofs;
|
||||
ofs.open(path, std::ofstream::out | std::ofstream::trunc);
|
||||
|
||||
|
||||
// d.sort();
|
||||
auto generator = puml::generator{diagram, d};
|
||||
|
||||
ofs << generator;
|
||||
|
||||
ofs.close();
|
||||
*/
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -20,16 +20,28 @@ struct message {
|
||||
std::string from;
|
||||
std::string from_usr;
|
||||
std::string to;
|
||||
std::string to_usr;
|
||||
std::string message;
|
||||
std::string return_type;
|
||||
unsigned int line;
|
||||
};
|
||||
|
||||
struct activity {
|
||||
std::string usr;
|
||||
std::string from;
|
||||
std::vector<message> messages;
|
||||
};
|
||||
|
||||
struct diagram {
|
||||
bool started{false};
|
||||
std::string name;
|
||||
std::vector<message> sequence;
|
||||
|
||||
//std::map<std::string, activity> sequences;
|
||||
std::map<std::string, activity> sequences;
|
||||
|
||||
void sort()
|
||||
{
|
||||
/*
|
||||
std::sort(sequence.begin(), sequence.end(),
|
||||
[](const auto &a, const auto &b) -> bool {
|
||||
if (a.from_usr == b.from_usr)
|
||||
@@ -37,6 +49,7 @@ struct diagram {
|
||||
|
||||
return a.from_usr > b.from_usr;
|
||||
});
|
||||
*/
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace clanguml {
|
||||
namespace visitor {
|
||||
namespace sequence_diagram {
|
||||
|
||||
using clanguml::model::sequence_diagram::activity;
|
||||
using clanguml::model::sequence_diagram::diagram;
|
||||
using clanguml::model::sequence_diagram::message;
|
||||
using clanguml::model::sequence_diagram::message_t;
|
||||
@@ -67,31 +68,43 @@ static enum CXChildVisitResult translation_unit_visitor(
|
||||
unsigned int offset{};
|
||||
clang_getFileLocation(
|
||||
cursor.location(), &f, &line, &column, &offset);
|
||||
std::string file{clang_getCString(clang_getFileName(f))};
|
||||
|
||||
auto &d = ctx->d;
|
||||
if (referenced.kind() == CXCursor_CXXMethod) {
|
||||
if (sp_name.find("clanguml::") == 0) {
|
||||
if (true/*sp_name.find("clanguml::") == 0 ||
|
||||
clang_Location_isFromMainFile(cursor.location())*/) {
|
||||
// Get calling object
|
||||
std::string caller =
|
||||
ctx->current_method.semantic_parent().fully_qualified();
|
||||
if (caller.empty() &&
|
||||
clang_Location_isFromMainFile(cursor.location()) == 0)
|
||||
caller = "<MAIN>";
|
||||
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();
|
||||
}
|
||||
|
||||
std::string caller_usr = ctx->current_method.usr();
|
||||
auto caller_usr = ctx->current_method.usr();
|
||||
// Get called object
|
||||
std::string callee =
|
||||
cursor.referenced().semantic_parent().fully_qualified();
|
||||
auto callee =
|
||||
referenced.semantic_parent().fully_qualified();
|
||||
auto callee_usr = referenced.semantic_parent().usr();
|
||||
|
||||
// Get called method
|
||||
std::string called_message = cursor.spelling();
|
||||
auto called_message = cursor.spelling();
|
||||
|
||||
// Found method call: CXCursorKind () const
|
||||
spdlog::debug("Found method call at line {}:{} "
|
||||
"\n\tCURRENT_METHOD: {}\n\tFROM: {}\n\tTO: "
|
||||
"{}\n\tMESSAGE: {}",
|
||||
clang_getCString(clang_getFileName(f)), line,
|
||||
ctx->current_method.spelling(), caller, callee,
|
||||
called_message);
|
||||
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;
|
||||
m.type = message_t::kCall;
|
||||
@@ -99,16 +112,25 @@ static enum CXChildVisitResult translation_unit_visitor(
|
||||
m.from_usr = caller_usr;
|
||||
m.line = line;
|
||||
m.to = callee;
|
||||
m.to_usr = referenced.usr();
|
||||
m.message = called_message;
|
||||
m.return_type = referenced.type().result_type().spelling();
|
||||
|
||||
ctx->d.sequence.emplace_back(std::move(m));
|
||||
if (d.sequences.find(caller_usr) == d.sequences.end()) {
|
||||
activity a;
|
||||
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));
|
||||
}
|
||||
}
|
||||
else if (referenced.kind() == CXCursor_FunctionDecl) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
ret = CXChildVisit_Continue;
|
||||
ret = CXChildVisit_Recurse;
|
||||
break;
|
||||
}
|
||||
case CXCursor_Namespace: {
|
||||
|
||||
Reference in New Issue
Block a user