Fixed include diagram test cases
This commit is contained in:
@@ -1162,9 +1162,6 @@ void translation_unit_visitor::
|
|||||||
{
|
{
|
||||||
auto template_params = cx::util::parse_unexposed_template_params(
|
auto template_params = cx::util::parse_unexposed_template_params(
|
||||||
type_name, [this](const std::string &t) {
|
type_name, [this](const std::string &t) {
|
||||||
// auto full_type = ctx.get_name_with_namespace(t);
|
|
||||||
// if (full_type.has_value())
|
|
||||||
// return full_type.value().to_string();
|
|
||||||
return t;
|
return t;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1177,27 +1174,6 @@ void translation_unit_visitor::
|
|||||||
for (auto &r : relationships) {
|
for (auto &r : relationships) {
|
||||||
c.add_relationship({std::get<1>(r), std::get<0>(r)});
|
c.add_relationship({std::get<1>(r), std::get<0>(r)});
|
||||||
}
|
}
|
||||||
|
|
||||||
// const auto &primary_template_ref =
|
|
||||||
// static_cast<const cppast::cpp_class_template &>(
|
|
||||||
// tspec.value().primary_template().get(ctx.entity_index())[0].get())
|
|
||||||
// .class_();
|
|
||||||
|
|
||||||
// if (primary_template_ref.user_data()) {
|
|
||||||
// auto base_template_full_name =
|
|
||||||
// static_cast<const char *>(primary_template_ref.user_data());
|
|
||||||
// LOG_DBG("Primary template ref set to: {}",
|
|
||||||
// base_template_full_name);
|
|
||||||
// // Add template specialization/instantiation
|
|
||||||
// // relationship
|
|
||||||
// c.add_relationship(
|
|
||||||
// {relationship_t::kInstantiation, base_template_full_name});
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// LOG_DBG(
|
|
||||||
// "No user data for base template {}",
|
|
||||||
// primary_template_ref.name());
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool translation_unit_visitor::find_relationships_in_unexposed_template_params(
|
bool translation_unit_visitor::find_relationships_in_unexposed_template_params(
|
||||||
|
|||||||
@@ -31,4 +31,10 @@ template <> id_t to_id(const clang::TemplateSpecializationType &t)
|
|||||||
{
|
{
|
||||||
return t.getTemplateName().getAsTemplateDecl()->getID();
|
return t.getTemplateName().getAsTemplateDecl()->getID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <> id_t to_id(const std::filesystem::path &file)
|
||||||
|
{
|
||||||
|
return std::hash<std::string>{}(file.lexically_normal()) >> 3;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <clang/AST/RecursiveASTVisitor.h>
|
#include <clang/AST/RecursiveASTVisitor.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
class NamespaceDecl;
|
class NamespaceDecl;
|
||||||
@@ -49,4 +50,5 @@ template <> id_t to_id(const clang::EnumType &type);
|
|||||||
|
|
||||||
template <> id_t to_id(const clang::TemplateSpecializationType &type);
|
template <> id_t to_id(const clang::TemplateSpecializationType &type);
|
||||||
|
|
||||||
|
template <> id_t to_id(const std::filesystem::path &type);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
#include "util/error.h"
|
#include "util/error.h"
|
||||||
#include "util/util.h"
|
#include "util/util.h"
|
||||||
|
|
||||||
//#include <cppast/libclang_parser.hpp>
|
|
||||||
#include <clang/Frontend/CompilerInstance.h>
|
#include <clang/Frontend/CompilerInstance.h>
|
||||||
#include <clang/Tooling/CompilationDatabase.h>
|
#include <clang/Tooling/CompilationDatabase.h>
|
||||||
#include <clang/Tooling/Tooling.h>
|
#include <clang/Tooling/Tooling.h>
|
||||||
@@ -262,8 +261,6 @@ public:
|
|||||||
|
|
||||||
virtual void HandleTranslationUnit(clang::ASTContext &ast_context)
|
virtual void HandleTranslationUnit(clang::ASTContext &ast_context)
|
||||||
{
|
{
|
||||||
// const auto* tud = ast_context.getTranslationUnitDecl();
|
|
||||||
//// tud->dump();
|
|
||||||
visitor_.TraverseDecl(ast_context.getTranslationUnitDecl());
|
visitor_.TraverseDecl(ast_context.getTranslationUnitDecl());
|
||||||
visitor_.finalize();
|
visitor_.finalize();
|
||||||
}
|
}
|
||||||
@@ -280,14 +277,31 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
|
std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
|
||||||
clang::CompilerInstance &CI, clang::StringRef file)
|
clang::CompilerInstance &CI, clang::StringRef file) override
|
||||||
{
|
{
|
||||||
return std::make_unique<
|
return std::make_unique<
|
||||||
diagram_ast_consumer<DiagramModel, DiagramConfig, DiagramVisitor>>(
|
diagram_ast_consumer<DiagramModel, DiagramConfig, DiagramVisitor>>(
|
||||||
CI, diagram_, config_);
|
CI, diagram_, config_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool BeginSourceFileAction(clang::CompilerInstance &ci) override
|
||||||
|
{
|
||||||
|
if constexpr (std::is_same_v<DiagramModel,
|
||||||
|
clanguml::include_diagram::model::diagram>) {
|
||||||
|
auto find_includes_callback =
|
||||||
|
std::make_unique<typename DiagramVisitor::include_visitor>(
|
||||||
|
ci.getSourceManager(), diagram_, config_);
|
||||||
|
|
||||||
|
clang::Preprocessor &pp = ci.getPreprocessor();
|
||||||
|
|
||||||
|
pp.addPPCallbacks(std::move(find_includes_callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DiagramModel &diagram_;
|
DiagramModel &diagram_;
|
||||||
const DiagramConfig &config_;
|
const DiagramConfig &config_;
|
||||||
@@ -338,28 +352,12 @@ std::unique_ptr<DiagramModel> generate(
|
|||||||
std::back_inserter(translation_units));
|
std::back_inserter(translation_units));
|
||||||
}
|
}
|
||||||
|
|
||||||
// DiagramVisitor visitor(db, *diagram, config);
|
|
||||||
|
|
||||||
clang::tooling::ClangTool clang_tool(db, translation_units);
|
clang::tooling::ClangTool clang_tool(db, translation_units);
|
||||||
auto action_factory =
|
auto action_factory =
|
||||||
std::make_unique<diagram_action_visitor_factory<DiagramModel,
|
std::make_unique<diagram_action_visitor_factory<DiagramModel,
|
||||||
DiagramConfig, DiagramVisitor>>(*diagram, config);
|
DiagramConfig, DiagramVisitor>>(*diagram, config);
|
||||||
clang_tool.run(action_factory.get());
|
clang_tool.run(action_factory.get());
|
||||||
|
|
||||||
/*
|
|
||||||
cppast::cpp_entity_index idx;
|
|
||||||
auto logger =
|
|
||||||
verbose ? cppast::default_logger() : cppast::default_quiet_logger();
|
|
||||||
cppast::simple_file_parser<cppast::libclang_parser> parser{
|
|
||||||
type_safe::ref(idx), std::move(logger)};
|
|
||||||
|
|
||||||
// Process all matching translation units
|
|
||||||
DiagramVisitor ctx(idx, *diagram, config);
|
|
||||||
cppast::parse_files(parser, translation_units, db);
|
|
||||||
for (auto &file : parser.files())
|
|
||||||
ctx(file);
|
|
||||||
*/
|
|
||||||
|
|
||||||
diagram->set_complete(true);
|
diagram->set_complete(true);
|
||||||
|
|
||||||
return diagram;
|
return diagram;
|
||||||
|
|||||||
@@ -357,6 +357,10 @@ tvl::value_t paths_filter::match(
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Matching source paths doesn't make sens if they are not absolute
|
||||||
|
if(!p.is_absolute())
|
||||||
|
return {};
|
||||||
|
|
||||||
auto pp = p.fs_path(root_);
|
auto pp = p.fs_path(root_);
|
||||||
for (const auto &path : paths_) {
|
for (const auto &path : paths_) {
|
||||||
if (util::starts_with(pp, path))
|
if (util::starts_with(pp, path))
|
||||||
|
|||||||
@@ -238,8 +238,9 @@ private:
|
|||||||
// of matching elements
|
// of matching elements
|
||||||
for (const auto &template_root : roots_) {
|
for (const auto &template_root : roots_) {
|
||||||
auto template_ref = detail::get<ElementT>(cd, template_root);
|
auto template_ref = detail::get<ElementT>(cd, template_root);
|
||||||
if (template_ref.has_value())
|
if (template_ref.has_value()) {
|
||||||
matching_elements_.emplace(template_ref.value());
|
matching_elements_.emplace(template_ref.value());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(roots_.empty() == matching_elements_.empty());
|
assert(roots_.empty() == matching_elements_.empty());
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/clang_utils.h"
|
||||||
#include "common/model/diagram_element.h"
|
#include "common/model/diagram_element.h"
|
||||||
#include "common/model/nested_trait.h"
|
#include "common/model/nested_trait.h"
|
||||||
#include "common/model/path.h"
|
#include "common/model/path.h"
|
||||||
@@ -51,17 +52,20 @@ class source_file
|
|||||||
public:
|
public:
|
||||||
source_file() = default;
|
source_file() = default;
|
||||||
|
|
||||||
source_file(const std::filesystem::path &p)
|
explicit source_file(const std::filesystem::path &p)
|
||||||
{
|
{
|
||||||
set_path({p.parent_path().string()});
|
set_path({p.parent_path().string()});
|
||||||
set_name(p.filename());
|
set_name(p.filename());
|
||||||
is_absolute_ = p.is_absolute();
|
is_absolute_ = p.is_absolute();
|
||||||
|
set_id(common::to_id(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
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; }
|
||||||
|
|
||||||
|
bool is_absolute() const { return is_absolute_; }
|
||||||
|
|
||||||
void set_type(source_file_t type) { type_ = type; }
|
void set_type(source_file_t type) { type_ = type; }
|
||||||
|
|
||||||
source_file_t type() const { return type_; }
|
source_file_t type() const { return type_; }
|
||||||
@@ -71,6 +75,12 @@ public:
|
|||||||
source_file &operator=(const source_file &) = delete;
|
source_file &operator=(const source_file &) = delete;
|
||||||
source_file &operator=(source_file &&) = delete;
|
source_file &operator=(source_file &&) = delete;
|
||||||
|
|
||||||
|
bool operator==(const source_file &right) const
|
||||||
|
{
|
||||||
|
return (path_ == right.path_) && (name() == right.name()) &&
|
||||||
|
(type_ == right.type_);
|
||||||
|
}
|
||||||
|
|
||||||
const filesystem_path &path() const { return path_; }
|
const filesystem_path &path() const { return path_; }
|
||||||
|
|
||||||
std::string full_name(bool /*relative*/) const override
|
std::string full_name(bool /*relative*/) const override
|
||||||
|
|||||||
@@ -37,33 +37,36 @@ void generator::generate_relationships(
|
|||||||
|
|
||||||
namespace plantuml_common = clanguml::common::generators::plantuml;
|
namespace plantuml_common = clanguml::common::generators::plantuml;
|
||||||
|
|
||||||
// if (f.type() == common::model::source_file_t::kDirectory) {
|
if (f.type() == common::model::source_file_t::kDirectory) {
|
||||||
// util::for_each(f, [this, &ostr](const auto &file) {
|
util::for_each(f, [this, &ostr](const auto &file) {
|
||||||
// generate_relationships(
|
generate_relationships(
|
||||||
// dynamic_cast<const source_file &>(*file), ostr);
|
dynamic_cast<const source_file &>(*file), ostr);
|
||||||
// });
|
});
|
||||||
// }
|
}
|
||||||
// else {
|
else {
|
||||||
// util::for_each_if(
|
util::for_each_if(
|
||||||
// f.relationships(),
|
f.relationships(),
|
||||||
// [this](const auto &r) {
|
[this](const auto &r) {
|
||||||
// return m_model.should_include(r.type()) &&
|
return m_model.should_include(r.type()) &&
|
||||||
// util::contains(m_generated_aliases, r.destination());
|
util::contains(m_generated_aliases,
|
||||||
// },
|
m_model.get(r.destination()).value().get().alias());
|
||||||
// [&f, &ostr](const auto &r) {
|
},
|
||||||
// ostr << f.alias() << " "
|
[&f, &ostr, this](const auto &r) {
|
||||||
// << plantuml_common::to_plantuml(r.type(), r.style())
|
ostr << f.alias() << " "
|
||||||
// << " "
|
<< plantuml_common::to_plantuml(r.type(), r.style()) << " "
|
||||||
// << r.destination() << '\n';
|
<< m_model.get(r.destination()).value().get().alias()
|
||||||
// });
|
<< '\n';
|
||||||
// }
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void generator::generate(const source_file &f, std::ostream &ostr) const
|
void generator::generate(const source_file &f, std::ostream &ostr) const
|
||||||
{
|
{
|
||||||
LOG_DBG("Generating source_file {}", f.name());
|
|
||||||
|
|
||||||
if (f.type() == common::model::source_file_t::kDirectory) {
|
if (f.type() == common::model::source_file_t::kDirectory) {
|
||||||
|
LOG_DBG("Generating directory {}", f.name());
|
||||||
|
|
||||||
ostr << "folder \"" << f.name();
|
ostr << "folder \"" << f.name();
|
||||||
ostr << "\" as " << f.alias();
|
ostr << "\" as " << f.alias();
|
||||||
ostr << " {\n";
|
ostr << " {\n";
|
||||||
@@ -77,6 +80,8 @@ void generator::generate(const source_file &f, std::ostream &ostr) const
|
|||||||
m_generated_aliases.emplace(f.alias());
|
m_generated_aliases.emplace(f.alias());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
LOG_DBG("Generating file {}", f.name());
|
||||||
|
|
||||||
if (m_model.should_include(f)) {
|
if (m_model.should_include(f)) {
|
||||||
ostr << "file \"" << f.name() << "\" as " << f.alias();
|
ostr << "file \"" << f.name() << "\" as " << f.alias();
|
||||||
|
|
||||||
@@ -95,14 +100,14 @@ void generator::generate(std::ostream &ostr) const
|
|||||||
{
|
{
|
||||||
ostr << "@startuml" << '\n';
|
ostr << "@startuml" << '\n';
|
||||||
|
|
||||||
generate_plantuml_directives(ostr, m_config.puml().before);
|
if (m_config.puml)
|
||||||
|
generate_plantuml_directives(ostr, m_config.puml().before);
|
||||||
|
|
||||||
// Generate files and folders
|
// Generate files and folders
|
||||||
util::for_each_if(
|
util::for_each_if(
|
||||||
m_model,
|
m_model,
|
||||||
[this](const auto &f) {
|
[this](const auto &f) {
|
||||||
return f->type() == common::model::source_file_t::kDirectory ||
|
return true;
|
||||||
m_model.should_include(*f);
|
|
||||||
},
|
},
|
||||||
[this, &ostr](const auto &f) {
|
[this, &ostr](const auto &f) {
|
||||||
generate(dynamic_cast<source_file &>(*f), ostr);
|
generate(dynamic_cast<source_file &>(*f), ostr);
|
||||||
@@ -115,7 +120,8 @@ void generator::generate(std::ostream &ostr) const
|
|||||||
|
|
||||||
generate_config_layout_hints(ostr);
|
generate_config_layout_hints(ostr);
|
||||||
|
|
||||||
generate_plantuml_directives(ostr, m_config.puml().after);
|
if (m_config.puml)
|
||||||
|
generate_plantuml_directives(ostr, m_config.puml().after);
|
||||||
|
|
||||||
ostr << "@enduml" << '\n';
|
ostr << "@enduml" << '\n';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,33 +35,50 @@ common::optional_ref<common::model::diagram_element> diagram::get(
|
|||||||
}
|
}
|
||||||
|
|
||||||
common::optional_ref<common::model::diagram_element> diagram::get(
|
common::optional_ref<common::model::diagram_element> diagram::get(
|
||||||
const common::model::diagram_element::id_t /*id*/) const
|
const common::model::diagram_element::id_t id) const
|
||||||
{
|
{
|
||||||
return {};
|
return get_file(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void diagram::add_file(std::unique_ptr<common::model::source_file> &&f)
|
void diagram::add_file(std::unique_ptr<common::model::source_file> &&f)
|
||||||
{
|
{
|
||||||
LOG_DBG("Adding source file: {}, {}", f->name(), f->full_name(true));
|
// Don't add the same file more than once
|
||||||
|
if(get_file(f->id()))
|
||||||
|
return;
|
||||||
|
|
||||||
files_.emplace_back(*f);
|
LOG_DBG("Adding source file: {}, {}", f->name(), f->fs_path().string());
|
||||||
|
|
||||||
auto p = f->path();
|
auto &ff = *f;
|
||||||
|
|
||||||
|
assert(!ff.name().empty());
|
||||||
|
assert(ff.id() != 0);
|
||||||
|
|
||||||
|
files_.emplace_back(ff);
|
||||||
|
|
||||||
|
auto p = ff.path();
|
||||||
|
|
||||||
if (!f->path().is_empty()) {
|
if (!f->path().is_empty()) {
|
||||||
// If the parent path is not empty, ensure relative parent directories
|
// If the parent path is not empty, ensure relative parent directories
|
||||||
// of this source_file are in the diagram
|
// of this source_file are in the diagram
|
||||||
common::model::filesystem_path parent_path_so_far;
|
common::model::filesystem_path parent_path_so_far;
|
||||||
for (const auto &directory : f->path()) {
|
for (const auto &directory : f->path()) {
|
||||||
auto dir = std::make_unique<common::model::source_file>();
|
auto source_file_path = parent_path_so_far | directory;
|
||||||
if (!parent_path_so_far.is_empty())
|
if(parent_path_so_far.is_empty())
|
||||||
dir->set_path(parent_path_so_far);
|
source_file_path = {directory};
|
||||||
dir->set_name(directory);
|
|
||||||
|
auto dir = std::make_unique<common::model::source_file>(
|
||||||
|
std::filesystem::path{source_file_path.to_string()});
|
||||||
dir->set_type(common::model::source_file_t::kDirectory);
|
dir->set_type(common::model::source_file_t::kDirectory);
|
||||||
|
|
||||||
if (!get_element(parent_path_so_far | directory).has_value())
|
assert(!dir->name().empty());
|
||||||
|
|
||||||
|
if (!get_element(source_file_path).has_value()) {
|
||||||
add_file(std::move(dir));
|
add_file(std::move(dir));
|
||||||
|
|
||||||
|
LOG_DBG("Added directory '{}' at path '{}'", directory,
|
||||||
|
parent_path_so_far.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
parent_path_so_far.append(directory);
|
parent_path_so_far.append(directory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,6 +98,18 @@ common::optional_ref<common::model::source_file> diagram::get_file(
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
common::optional_ref<common::model::source_file> diagram::get_file(
|
||||||
|
const common::model::diagram_element::id_t id) const
|
||||||
|
{
|
||||||
|
for (const auto &p : files_) {
|
||||||
|
if (p.get().id() == id) {
|
||||||
|
return {p};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
std::string diagram::to_alias(const std::string &full_name) const
|
std::string diagram::to_alias(const std::string &full_name) const
|
||||||
{
|
{
|
||||||
LOG_DBG("Looking for alias for {}", full_name);
|
LOG_DBG("Looking for alias for {}", full_name);
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ public:
|
|||||||
common::optional_ref<common::model::source_file> get_file(
|
common::optional_ref<common::model::source_file> get_file(
|
||||||
const std::string &name) const;
|
const std::string &name) const;
|
||||||
|
|
||||||
|
common::optional_ref<common::model::source_file> get_file(
|
||||||
|
const common::model::diagram_element::id_t id) const;
|
||||||
|
|
||||||
std::string to_alias(const std::string &full_name) const;
|
std::string to_alias(const std::string &full_name) const;
|
||||||
|
|
||||||
const common::reference_vector<common::model::source_file> &files() const;
|
const common::reference_vector<common::model::source_file> &files() const;
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
#include "translation_unit_visitor.h"
|
#include "translation_unit_visitor.h"
|
||||||
|
|
||||||
|
#include "common/clang_utils.h"
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
namespace clanguml::include_diagram::visitor {
|
namespace clanguml::include_diagram::visitor {
|
||||||
@@ -31,4 +33,203 @@ translation_unit_visitor::translation_unit_visitor(clang::SourceManager &sm,
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
translation_unit_visitor::include_visitor::include_visitor(
|
||||||
|
clang::SourceManager &sm,
|
||||||
|
clanguml::include_diagram::model::diagram &diagram,
|
||||||
|
const clanguml::config::include_diagram &config)
|
||||||
|
: source_manager_{sm}
|
||||||
|
, diagram_{diagram}
|
||||||
|
, config_{config}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void translation_unit_visitor::include_visitor::InclusionDirective(
|
||||||
|
clang::SourceLocation hash_loc, const clang::Token &include_tok,
|
||||||
|
clang::StringRef file_name, bool is_angled,
|
||||||
|
clang::CharSourceRange filename_range, const clang::FileEntry *file,
|
||||||
|
clang::StringRef search_path, clang::StringRef relative_path,
|
||||||
|
const clang::Module *imported, clang::SrcMgr::CharacteristicKind file_type)
|
||||||
|
{
|
||||||
|
using common::model::relationship;
|
||||||
|
using common::model::source_file;
|
||||||
|
using common::model::source_file_t;
|
||||||
|
|
||||||
|
auto current_file =
|
||||||
|
std::filesystem::path{source_manager_.getFilename(hash_loc).str()};
|
||||||
|
current_file = std::filesystem::absolute(current_file);
|
||||||
|
current_file = current_file.lexically_normal();
|
||||||
|
|
||||||
|
auto current_file_id = process_source_file(current_file);
|
||||||
|
if (!current_file_id)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert(diagram().get(current_file_id.value()));
|
||||||
|
|
||||||
|
auto include_path = std::filesystem::path(file->getDir()->getName().str());
|
||||||
|
include_path = include_path / file->getName().str();
|
||||||
|
include_path = include_path.lexically_normal();
|
||||||
|
|
||||||
|
LOG_DBG("Processing include file {} in file {}", include_path.string(),
|
||||||
|
current_file.string());
|
||||||
|
|
||||||
|
auto relative_include_path = include_path;
|
||||||
|
if (config().relative_to) {
|
||||||
|
const std::filesystem::path relative_to{config().relative_to()};
|
||||||
|
relative_include_path =
|
||||||
|
std::filesystem::relative(include_path, relative_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diagram().should_include(source_file{include_path})) {
|
||||||
|
process_internal_header(include_path,
|
||||||
|
file_type != clang::SrcMgr::CharacteristicKind::C_User,
|
||||||
|
current_file_id.value());
|
||||||
|
}
|
||||||
|
else if (config().generate_system_headers() && is_angled) {
|
||||||
|
process_external_system_header(
|
||||||
|
relative_path.str(), current_file_id.value());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LOG_DBG("Skipping include directive to file {}", include_path.string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<common::id_t>
|
||||||
|
translation_unit_visitor::include_visitor::process_internal_header(
|
||||||
|
const std::filesystem::path &include_path, bool is_system,
|
||||||
|
const common::id_t current_file_id)
|
||||||
|
{
|
||||||
|
// Relativize the path with respect to relative_to config option
|
||||||
|
auto relative_include_path = include_path;
|
||||||
|
if (config().relative_to) {
|
||||||
|
const std::filesystem::path relative_to{config().relative_to()};
|
||||||
|
relative_include_path =
|
||||||
|
std::filesystem::relative(include_path, relative_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this source file is already registered in the diagram,
|
||||||
|
// if not add it
|
||||||
|
auto diagram_path =
|
||||||
|
common::model::source_file{relative_include_path}.full_path();
|
||||||
|
if (!diagram().get_element(diagram_path.to_string()).has_value()) {
|
||||||
|
diagram().add_file(std::make_unique<common::model::source_file>(
|
||||||
|
diagram_path.to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &include_file = diagram().get_element(diagram_path).value();
|
||||||
|
|
||||||
|
include_file.set_type(common::model::source_file_t::kHeader);
|
||||||
|
include_file.set_file(
|
||||||
|
std::filesystem::absolute(include_path).lexically_normal().string());
|
||||||
|
include_file.set_line(0);
|
||||||
|
|
||||||
|
// Add relationship from the currently parsed source file to this
|
||||||
|
// include file
|
||||||
|
const auto relationship_type = is_system
|
||||||
|
? common::model::relationship_t::kDependency
|
||||||
|
: common::model::relationship_t::kAssociation;
|
||||||
|
|
||||||
|
if (diagram().get(current_file_id)) {
|
||||||
|
diagram()
|
||||||
|
.get(current_file_id)
|
||||||
|
.value()
|
||||||
|
.get()
|
||||||
|
.add_relationship(common::model::relationship{
|
||||||
|
relationship_type, include_file.id()});
|
||||||
|
}
|
||||||
|
|
||||||
|
return include_file.id();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<common::id_t>
|
||||||
|
translation_unit_visitor::include_visitor::process_external_system_header(
|
||||||
|
const std::filesystem::path &include_path,
|
||||||
|
const common::id_t current_file_id)
|
||||||
|
{
|
||||||
|
const auto file_name = include_path.filename();
|
||||||
|
const auto file_name_str = file_name.string();
|
||||||
|
|
||||||
|
auto f = std::make_unique<common::model::source_file>();
|
||||||
|
f->set_name(include_path.string());
|
||||||
|
f->set_type(common::model::source_file_t::kHeader);
|
||||||
|
f->set_id(common::to_id(include_path));
|
||||||
|
|
||||||
|
const auto f_id = f->id();
|
||||||
|
|
||||||
|
diagram().add_file(std::move(f));
|
||||||
|
|
||||||
|
if (diagram().get(current_file_id)) {
|
||||||
|
diagram()
|
||||||
|
.get(current_file_id)
|
||||||
|
.value()
|
||||||
|
.get()
|
||||||
|
.add_relationship(common::model::relationship{
|
||||||
|
common::model::relationship_t::kDependency, f_id});
|
||||||
|
}
|
||||||
|
|
||||||
|
return f_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<common::id_t>
|
||||||
|
translation_unit_visitor::include_visitor::process_source_file(
|
||||||
|
const std::filesystem::path &file)
|
||||||
|
{
|
||||||
|
using common::model::relationship;
|
||||||
|
using common::model::source_file;
|
||||||
|
using common::model::source_file_t;
|
||||||
|
|
||||||
|
LOG_DBG("Processing source file {}", file.string());
|
||||||
|
|
||||||
|
auto file_path = std::filesystem::path{file};
|
||||||
|
|
||||||
|
// Make sure the file_path is absolute with respect to the
|
||||||
|
// filesystem, and in normal form
|
||||||
|
if (file_path.is_relative()) {
|
||||||
|
file_path = config().base_directory() / file_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_path = file_path.lexically_normal();
|
||||||
|
|
||||||
|
// Relativize the path with respect to relative_to config option
|
||||||
|
auto relative_file_path = file_path;
|
||||||
|
if (config().relative_to) {
|
||||||
|
const std::filesystem::path relative_to{config().relative_to()};
|
||||||
|
relative_file_path = std::filesystem::relative(file_path, relative_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diagram().should_include(source_file{file_path})) {
|
||||||
|
[[maybe_unused]] const auto relative_file_path_str =
|
||||||
|
relative_file_path.string();
|
||||||
|
|
||||||
|
// 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 (!diagram().get_element(diagram_path).has_value()) {
|
||||||
|
diagram().add_file(
|
||||||
|
std::make_unique<source_file>(relative_file_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &source_file = 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);
|
||||||
|
|
||||||
|
source_file.set_file(std::filesystem::absolute(file.string())
|
||||||
|
.lexically_normal()
|
||||||
|
.string());
|
||||||
|
source_file.set_line(0);
|
||||||
|
|
||||||
|
return source_file.id();
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Skipping source file {}", file_path.string());
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include <clang/AST/RecursiveASTVisitor.h>
|
#include <clang/AST/RecursiveASTVisitor.h>
|
||||||
#include <clang/Basic/SourceManager.h>
|
#include <clang/Basic/SourceManager.h>
|
||||||
|
#include <clang/Lex/PPCallbacks.h>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
@@ -36,11 +37,57 @@ namespace clanguml::include_diagram::visitor {
|
|||||||
class translation_unit_visitor
|
class translation_unit_visitor
|
||||||
: public clang::RecursiveASTVisitor<translation_unit_visitor> {
|
: public clang::RecursiveASTVisitor<translation_unit_visitor> {
|
||||||
public:
|
public:
|
||||||
|
// This is an internal class for convenience to be able to access the
|
||||||
|
// include_visitor type from translation_unit_visitor type
|
||||||
|
class include_visitor : public clang::PPCallbacks {
|
||||||
|
public:
|
||||||
|
include_visitor(clang::SourceManager &sm,
|
||||||
|
clanguml::include_diagram::model::diagram &diagram,
|
||||||
|
const clanguml::config::include_diagram &config);
|
||||||
|
|
||||||
|
void InclusionDirective(clang::SourceLocation hash_loc,
|
||||||
|
const clang::Token &include_tok, clang::StringRef file_name,
|
||||||
|
bool is_angled, clang::CharSourceRange filename_range,
|
||||||
|
const clang::FileEntry *file, clang::StringRef search_path,
|
||||||
|
clang::StringRef relative_path, const clang::Module *imported,
|
||||||
|
clang::SrcMgr::CharacteristicKind file_type) override;
|
||||||
|
|
||||||
|
std::optional<common::id_t> process_internal_header(const std::filesystem::path &include_path,
|
||||||
|
bool is_system, const common::id_t current_file_id);
|
||||||
|
|
||||||
|
std::optional<common::id_t> process_external_system_header(
|
||||||
|
const std::filesystem::path &include_path,
|
||||||
|
const common::id_t current_file_id);
|
||||||
|
|
||||||
|
std::optional<common::id_t> process_source_file(const std::filesystem::path &file);
|
||||||
|
|
||||||
|
clanguml::include_diagram::model::diagram &diagram()
|
||||||
|
{
|
||||||
|
return diagram_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const clanguml::config::include_diagram &config() const
|
||||||
|
{
|
||||||
|
return config_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
clang::SourceManager &source_manager_;
|
||||||
|
|
||||||
|
// Reference to the output diagram model
|
||||||
|
clanguml::include_diagram::model::diagram &diagram_;
|
||||||
|
|
||||||
|
// Reference to class diagram config
|
||||||
|
const clanguml::config::include_diagram &config_;
|
||||||
|
};
|
||||||
|
|
||||||
translation_unit_visitor(clang::SourceManager &sm,
|
translation_unit_visitor(clang::SourceManager &sm,
|
||||||
clanguml::include_diagram::model::diagram &diagram,
|
clanguml::include_diagram::model::diagram &diagram,
|
||||||
const clanguml::config::include_diagram &config);
|
const clanguml::config::include_diagram &config);
|
||||||
|
|
||||||
void operator()(const cppast::cpp_entity &file);
|
clanguml::include_diagram::model::diagram &diagram() { return diagram_; }
|
||||||
|
|
||||||
|
const clanguml::config::include_diagram &config() const { return config_; }
|
||||||
|
|
||||||
void finalize() { }
|
void finalize() { }
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ diagrams:
|
|||||||
- ../../tests/t40001/**/*.cc
|
- ../../tests/t40001/**/*.cc
|
||||||
- ../../tests/t40001/**/*.h
|
- ../../tests/t40001/**/*.h
|
||||||
# Render the paths relative to this directory
|
# Render the paths relative to this directory
|
||||||
relative_to: ../../tests/t40001
|
relative_to: ../../../tests/t40001
|
||||||
# Include also external system headers
|
# Include also external system headers
|
||||||
generate_system_headers: true
|
generate_system_headers: true
|
||||||
include:
|
include:
|
||||||
# Include only headers belonging to these paths
|
# Include only headers belonging to these paths
|
||||||
paths:
|
paths:
|
||||||
- ../../tests/t40001
|
- ../../../tests/t40001
|
||||||
plantuml:
|
plantuml:
|
||||||
before:
|
before:
|
||||||
- "' t40001 test include diagram"
|
- "' t40001 test include diagram"
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ TEST_CASE("t40001", "[test-case][package]")
|
|||||||
|
|
||||||
REQUIRE(diagram->name == "t40001_include");
|
REQUIRE(diagram->name == "t40001_include");
|
||||||
|
|
||||||
auto model = generate_include_diagram(db, diagram);
|
auto model = generate_include_diagram(*db, diagram);
|
||||||
|
|
||||||
REQUIRE(model->name() == "t40001_include");
|
REQUIRE(model->name() == "t40001_include");
|
||||||
|
|
||||||
|
|||||||
@@ -9,15 +9,15 @@ diagrams:
|
|||||||
- ../../tests/t40002/**/*.cc
|
- ../../tests/t40002/**/*.cc
|
||||||
- ../../tests/t40002/**/*.h
|
- ../../tests/t40002/**/*.h
|
||||||
# Render the paths relative to this directory
|
# Render the paths relative to this directory
|
||||||
relative_to: ../../tests/t40002
|
relative_to: ../../../tests/t40002
|
||||||
include:
|
include:
|
||||||
# Include only files belonging to these paths
|
# Include only files belonging to these paths
|
||||||
paths:
|
paths:
|
||||||
- ../../tests/t40002
|
- ../../../tests/t40002
|
||||||
exclude:
|
exclude:
|
||||||
paths:
|
paths:
|
||||||
# Exclude single header
|
# Exclude single header
|
||||||
- ../../tests/t40002/include/lib2/lib2_detail.h
|
- ../../../tests/t40002/include/lib2/lib2_detail.h
|
||||||
plantuml:
|
plantuml:
|
||||||
before:
|
before:
|
||||||
- "' t40002 test include diagram"
|
- "' t40002 test include diagram"
|
||||||
@@ -24,7 +24,7 @@ TEST_CASE("t40002", "[test-case][package]")
|
|||||||
|
|
||||||
REQUIRE(diagram->name == "t40002_include");
|
REQUIRE(diagram->name == "t40002_include");
|
||||||
|
|
||||||
auto model = generate_include_diagram(db, diagram);
|
auto model = generate_include_diagram(*db, diagram);
|
||||||
|
|
||||||
REQUIRE(model->name() == "t40002_include");
|
REQUIRE(model->name() == "t40002_include");
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,14 @@ diagrams:
|
|||||||
- ../../tests/t40003/include/**/*.h
|
- ../../tests/t40003/include/**/*.h
|
||||||
- ../../tests/t40003/src/**/*.cc
|
- ../../tests/t40003/src/**/*.cc
|
||||||
# Render the paths relative to this directory
|
# Render the paths relative to this directory
|
||||||
relative_to: ../../tests/t40003
|
relative_to: ../../../tests/t40003
|
||||||
include:
|
include:
|
||||||
# Include only files belonging to these paths
|
# Include only files which depend on t1.h
|
||||||
dependants:
|
dependants:
|
||||||
- ../../tests/t40003/include/dependants/t1.h
|
- ../../../tests/t40003/include/dependants/t1.h
|
||||||
|
# and dependencies of t2.cc
|
||||||
dependencies:
|
dependencies:
|
||||||
- ../../tests/t40003/src/dependencies/t2.cc
|
- ../../../tests/t40003/src/dependencies/t2.cc
|
||||||
plantuml:
|
plantuml:
|
||||||
before:
|
before:
|
||||||
- "' t40003 test include diagram"
|
- "' t40003 test include diagram"
|
||||||
@@ -24,7 +24,7 @@ TEST_CASE("t40003", "[test-case][package]")
|
|||||||
|
|
||||||
REQUIRE(diagram->name == "t40003_include");
|
REQUIRE(diagram->name == "t40003_include");
|
||||||
|
|
||||||
auto model = generate_include_diagram(db, diagram);
|
auto model = generate_include_diagram(*db, diagram);
|
||||||
|
|
||||||
REQUIRE(model->name() == "t40003_include");
|
REQUIRE(model->name() == "t40003_include");
|
||||||
|
|
||||||
|
|||||||
@@ -257,9 +257,9 @@ using namespace clanguml::test::matchers;
|
|||||||
////
|
////
|
||||||
//// Include diagram tests
|
//// Include diagram tests
|
||||||
////
|
////
|
||||||
//#include "t40001/test_case.h"
|
#include "t40001/test_case.h"
|
||||||
//#include "t40002/test_case.h"
|
#include "t40002/test_case.h"
|
||||||
//#include "t40003/test_case.h"
|
#include "t40003/test_case.h"
|
||||||
//
|
//
|
||||||
////
|
////
|
||||||
//// Other tests (e.g. configuration file)
|
//// Other tests (e.g. configuration file)
|
||||||
|
|||||||
Reference in New Issue
Block a user