Refactored include diagram visitors
This commit is contained in:
@@ -50,6 +50,13 @@ class source_file
|
|||||||
public:
|
public:
|
||||||
source_file() = default;
|
source_file() = default;
|
||||||
|
|
||||||
|
source_file(const std::filesystem::path &p)
|
||||||
|
{
|
||||||
|
set_path(p.parent_path().string());
|
||||||
|
set_name(p.filename());
|
||||||
|
is_absolute_ = p.is_absolute();
|
||||||
|
}
|
||||||
|
|
||||||
void set_path(const filesystem_path &p) { path_ = p; }
|
void set_path(const filesystem_path &p) { path_ = p; }
|
||||||
|
|
||||||
void set_absolute() { is_absolute_ = true; }
|
void set_absolute() { is_absolute_ = true; }
|
||||||
@@ -70,6 +77,8 @@ public:
|
|||||||
return (path_ | name()).to_string();
|
return (path_ | name()).to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto full_path() const { return path() | name(); }
|
||||||
|
|
||||||
void add_file(std::unique_ptr<source_file> &&f)
|
void add_file(std::unique_ptr<source_file> &&f)
|
||||||
{
|
{
|
||||||
LOG_DBG("Adding source file: {}, {}", f->name(), f->full_name(true));
|
LOG_DBG("Adding source file: {}, {}", f->name(), f->full_name(true));
|
||||||
|
|||||||
@@ -42,6 +42,24 @@ void diagram::add_file(std::unique_ptr<common::model::source_file> &&f)
|
|||||||
|
|
||||||
auto p = f->path();
|
auto p = f->path();
|
||||||
|
|
||||||
|
if (!f->path().is_empty()) {
|
||||||
|
// If the parent path is not empty, ensure relative parent directories
|
||||||
|
// of this source_file are in the diagram
|
||||||
|
common::model::filesystem_path parent_path_so_far;
|
||||||
|
for (const auto &directory : f->path()) {
|
||||||
|
auto dir = std::make_unique<common::model::source_file>();
|
||||||
|
if (!parent_path_so_far.is_empty())
|
||||||
|
dir->set_path(parent_path_so_far);
|
||||||
|
dir->set_name(directory);
|
||||||
|
dir->set_type(common::model::source_file_t::kDirectory);
|
||||||
|
|
||||||
|
if (!get_element(parent_path_so_far | directory).has_value())
|
||||||
|
add_file(std::move(dir));
|
||||||
|
|
||||||
|
parent_path_so_far.append(directory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
add_element(p, std::move(f));
|
add_element(p, std::move(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,125 +37,132 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file)
|
|||||||
{
|
{
|
||||||
assert(file.kind() == cppast::cpp_entity_kind::file_t);
|
assert(file.kind() == cppast::cpp_entity_kind::file_t);
|
||||||
|
|
||||||
const auto &f = static_cast<const cppast::cpp_file &>(file);
|
process_source_file(static_cast<const cppast::cpp_file &>(file));
|
||||||
|
|
||||||
auto file_path = f.name();
|
|
||||||
|
|
||||||
LOG_DBG("Processing source file {}", file_path);
|
|
||||||
|
|
||||||
process_file(file_path, true);
|
|
||||||
|
|
||||||
cppast::visit(file,
|
cppast::visit(file,
|
||||||
[&, this](const cppast::cpp_entity &e, cppast::visitor_info info) {
|
[&, this](const cppast::cpp_entity &e, cppast::visitor_info info) {
|
||||||
if (e.kind() == cppast::cpp_entity_kind::include_directive_t) {
|
if (e.kind() == cppast::cpp_entity_kind::include_directive_t) {
|
||||||
assert(ctx.get_current_file().has_value());
|
|
||||||
|
|
||||||
auto file_path_cpy = file.name();
|
|
||||||
|
|
||||||
const auto &inc =
|
const auto &inc =
|
||||||
static_cast<const cppast::cpp_include_directive &>(e);
|
static_cast<const cppast::cpp_include_directive &>(e);
|
||||||
|
|
||||||
LOG_DBG("Processing include directive {} in file {}",
|
process_include_directive(inc);
|
||||||
inc.full_path(), ctx.get_current_file().value().name());
|
|
||||||
|
|
||||||
process_file(inc.full_path(), false, inc.include_kind());
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void translation_unit_visitor::process_file(const std::string &file,
|
void translation_unit_visitor::process_include_directive(
|
||||||
bool register_as_current,
|
const cppast::cpp_include_directive &include_directive)
|
||||||
std::optional<cppast::cpp_include_kind> include_kind)
|
|
||||||
{
|
{
|
||||||
auto include_path = std::filesystem::path(file);
|
using common::model::relationship;
|
||||||
|
using common::model::source_file;
|
||||||
|
using common::model::source_file_t;
|
||||||
|
|
||||||
const std::filesystem::path base_directory{ctx.config().base_directory()};
|
assert(ctx.get_current_file().has_value());
|
||||||
|
|
||||||
|
LOG_DBG("Processing include directive {} in file {}",
|
||||||
|
include_directive.full_path(), ctx.get_current_file().value().name());
|
||||||
|
|
||||||
|
auto include_path = std::filesystem::path(include_directive.full_path());
|
||||||
|
|
||||||
|
// Make sure the include_path is absolute with respect to the
|
||||||
|
// filesystem, and in normal form
|
||||||
if (include_path.is_relative()) {
|
if (include_path.is_relative()) {
|
||||||
include_path = ctx.config().base_directory() / include_path;
|
include_path = ctx.config().base_directory() / include_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
include_path = include_path.lexically_normal();
|
include_path = include_path.lexically_normal();
|
||||||
|
|
||||||
std::string full_file_path = include_path;
|
if (ctx.diagram().should_include(source_file{include_path})) {
|
||||||
auto f_abs = std::make_unique<common::model::source_file>();
|
// Relativize the path with respect to relative_to config option
|
||||||
|
auto relative_include_path = include_path;
|
||||||
if (include_path.is_absolute())
|
|
||||||
f_abs->set_absolute();
|
|
||||||
|
|
||||||
f_abs->set_path(include_path.parent_path().string());
|
|
||||||
f_abs->set_name(include_path.filename().string());
|
|
||||||
|
|
||||||
if (ctx.diagram().should_include(*f_abs)) {
|
|
||||||
if (ctx.config().relative_to) {
|
if (ctx.config().relative_to) {
|
||||||
const std::filesystem::path relative_to{ctx.config().relative_to()};
|
const std::filesystem::path relative_to{ctx.config().relative_to()};
|
||||||
|
relative_include_path =
|
||||||
include_path = std::filesystem::relative(include_path, relative_to);
|
std::filesystem::relative(include_path, relative_to);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto f = std::make_unique<common::model::source_file>();
|
// Check if this source file is already registered in the diagram,
|
||||||
|
// if not add it
|
||||||
auto parent_path_str = include_path.parent_path().string();
|
auto diagram_path = source_file{relative_include_path}.full_path();
|
||||||
if (!parent_path_str.empty())
|
if (!ctx.diagram().get_element(diagram_path).has_value()) {
|
||||||
f->set_path(parent_path_str);
|
ctx.diagram().add_file(
|
||||||
f->set_name(include_path.filename().string());
|
std::make_unique<source_file>(relative_include_path));
|
||||||
f->set_type(common::model::source_file_t::kHeader);
|
|
||||||
|
|
||||||
if (register_as_current &&
|
|
||||||
ctx.diagram().get_element(f->path() | f->name()).has_value()) {
|
|
||||||
// This file is already in the diagram (e.g. it was added through an
|
|
||||||
// include directive before it was visited by the parser)
|
|
||||||
ctx.set_current_file(
|
|
||||||
ctx.diagram().get_element(f->path() | f->name()));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!f->path().is_empty()) {
|
auto &include_file = ctx.diagram().get_element(diagram_path).value();
|
||||||
// Ensure parent path directories source_files
|
|
||||||
// are in the diagram
|
|
||||||
common::model::filesystem_path parent_path_so_far;
|
|
||||||
for (const auto &directory : f->path()) {
|
|
||||||
auto dir = std::make_unique<common::model::source_file>();
|
|
||||||
if (!parent_path_so_far.is_empty())
|
|
||||||
dir->set_path(parent_path_so_far);
|
|
||||||
dir->set_name(directory);
|
|
||||||
dir->set_type(common::model::source_file_t::kDirectory);
|
|
||||||
|
|
||||||
if (!ctx.diagram()
|
include_file.set_type(source_file_t::kHeader);
|
||||||
.get_element(parent_path_so_far | directory)
|
|
||||||
.has_value())
|
|
||||||
ctx.diagram().add_file(std::move(dir));
|
|
||||||
|
|
||||||
parent_path_so_far.append(directory);
|
// Add relationship from the currently parsed source file to this
|
||||||
}
|
// include file
|
||||||
}
|
auto relationship_type = common::model::relationship_t::kAssociation;
|
||||||
|
if (include_directive.include_kind() ==
|
||||||
auto diagram_file_path = f->path() | f->name();
|
cppast::cpp_include_kind::system)
|
||||||
|
|
||||||
if (!ctx.diagram().get_element(diagram_file_path).has_value()) {
|
|
||||||
ctx.diagram().add_file(std::move(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto &diagram_source_file =
|
|
||||||
ctx.diagram().get_element(diagram_file_path).value();
|
|
||||||
|
|
||||||
if (!register_as_current) {
|
|
||||||
auto relationship_type =
|
|
||||||
common::model::relationship_t::kAssociation;
|
|
||||||
if (include_kind.has_value() &&
|
|
||||||
include_kind.value() == cppast::cpp_include_kind::system)
|
|
||||||
relationship_type = common::model::relationship_t::kDependency;
|
relationship_type = common::model::relationship_t::kDependency;
|
||||||
|
|
||||||
ctx.get_current_file().value().add_relationship(
|
ctx.get_current_file().value().add_relationship(
|
||||||
common::model::relationship{
|
relationship{relationship_type, include_file.alias()});
|
||||||
relationship_type, diagram_source_file.alias()});
|
|
||||||
|
|
||||||
auto fp = std::filesystem::absolute(file).lexically_normal();
|
include_file.set_file(std::filesystem::absolute(include_path)
|
||||||
diagram_source_file.set_file(fp.string());
|
.lexically_normal()
|
||||||
diagram_source_file.set_line(0);
|
.string());
|
||||||
|
include_file.set_line(0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LOG_DBG("Skipping include directive to file {}", include_path.string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void translation_unit_visitor::process_source_file(const cppast::cpp_file &file)
|
||||||
|
{
|
||||||
|
using common::model::relationship;
|
||||||
|
using common::model::source_file;
|
||||||
|
using common::model::source_file_t;
|
||||||
|
|
||||||
|
LOG_DBG("Processing source file {}", file.name());
|
||||||
|
|
||||||
|
auto file_path = std::filesystem::path(file.name());
|
||||||
|
|
||||||
|
// Make sure the file_path is absolute with respect to the
|
||||||
|
// filesystem, and in normal form
|
||||||
|
if (file_path.is_relative()) {
|
||||||
|
file_path = ctx.config().base_directory() / file_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.set_current_file(type_safe::opt_ref(diagram_source_file));
|
file_path = file_path.lexically_normal();
|
||||||
|
|
||||||
|
if (ctx.diagram().should_include(source_file{file_path})) {
|
||||||
|
// Relativize the path with respect to relative_to config option
|
||||||
|
auto relative_file_path = file_path;
|
||||||
|
if (ctx.config().relative_to) {
|
||||||
|
const std::filesystem::path relative_to{ctx.config().relative_to()};
|
||||||
|
relative_file_path =
|
||||||
|
std::filesystem::relative(file_path, relative_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this source file is already registered in the diagram,
|
||||||
|
// if not add it
|
||||||
|
auto diagram_path = source_file{relative_file_path}.full_path();
|
||||||
|
if (!ctx.diagram().get_element(diagram_path).has_value()) {
|
||||||
|
ctx.diagram().add_file(
|
||||||
|
std::make_unique<source_file>(relative_file_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &source_file = ctx.diagram().get_element(diagram_path).value();
|
||||||
|
|
||||||
|
const std::string implementation_suffix_prefix{".c"};
|
||||||
|
if (file_path.has_extension() &&
|
||||||
|
util::starts_with(
|
||||||
|
file_path.extension().string(), implementation_suffix_prefix)) {
|
||||||
|
source_file.set_type(source_file_t::kImplementation);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
source_file.set_type(source_file_t::kHeader);
|
||||||
|
|
||||||
|
ctx.set_current_file(type_safe::opt_ref(source_file));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LOG_DBG("Skipping source file {}", file_path.string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,8 +46,10 @@ public:
|
|||||||
void operator()(const cppast::cpp_entity &file);
|
void operator()(const cppast::cpp_entity &file);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void process_file(const std::string &file, bool register_as_current = false,
|
void process_include_directive(
|
||||||
std::optional<cppast::cpp_include_kind> include_kind = {});
|
const cppast::cpp_include_directive &include_directive);
|
||||||
|
|
||||||
|
void process_source_file(const cppast::cpp_file &file);
|
||||||
|
|
||||||
// ctx allows to track current visitor context, e.g. current namespace
|
// ctx allows to track current visitor context, e.g. current namespace
|
||||||
translation_unit_context ctx;
|
translation_unit_context ctx;
|
||||||
|
|||||||
@@ -230,5 +230,10 @@ bool starts_with(
|
|||||||
return pref == pat;
|
return pref == pat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <> bool starts_with(const std::string &s, const std::string &prefix)
|
||||||
|
{
|
||||||
|
return s.rfind(prefix, 0) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,6 +164,8 @@ template <>
|
|||||||
bool starts_with(
|
bool starts_with(
|
||||||
const std::filesystem::path &path, const std::filesystem::path &prefix);
|
const std::filesystem::path &path, const std::filesystem::path &prefix);
|
||||||
|
|
||||||
|
template <> bool starts_with(const std::string &s, const std::string &prefix);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool ends_with(const std::vector<T> &col, const std::vector<T> &suffix)
|
bool ends_with(const std::vector<T> &col, const std::vector<T> &suffix)
|
||||||
{
|
{
|
||||||
@@ -175,7 +177,7 @@ bool ends_with(const std::vector<T> &col, const std::vector<T> &suffix)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Removes prefix sequence of elements from the beggining of col.
|
* @brief Removes prefix sequence of elements from the beginning of col.
|
||||||
*
|
*
|
||||||
* @tparam T
|
* @tparam T
|
||||||
* @param col
|
* @param col
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include "../lib2/lib2.h"
|
#include "../lib2/lib2.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace clanguml::t40002::lib1 {
|
namespace clanguml::t40002::lib1 {
|
||||||
|
|
||||||
int foo0();
|
int foo0();
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../lib1/lib1.h"
|
|
||||||
|
|
||||||
namespace clanguml::t40002::lib2 {
|
namespace clanguml::t40002::lib2 {
|
||||||
|
|
||||||
int foo2();
|
int foo2();
|
||||||
|
|||||||
Reference in New Issue
Block a user