Added should_include helper methods in sequence diagram visitor

This commit is contained in:
Bartek Kryza 2022-12-18 22:18:26 +01:00
parent c4d8bddf96
commit 1c7e64c51e
6 changed files with 235 additions and 178 deletions

View File

@ -209,8 +209,11 @@ element_filter::element_filter(filter_t type, std::vector<std::string> elements)
tvl::value_t element_filter::match(
const diagram & /*d*/, const element &e) const
{
return tvl::any_of(elements_.begin(), elements_.end(),
[&e](const auto &el) { return e.full_name(false) == el; });
return tvl::any_of(
elements_.begin(), elements_.end(), [&e](const auto &el) {
return (e.full_name(false) == el) ||
(fmt::format("::{}", e.full_name(false)) == el);
});
}
subclass_filter::subclass_filter(filter_t type, std::vector<std::string> roots)

View File

@ -209,87 +209,6 @@ int main(int argc, const char *argv[])
return 0;
}
void generate_diagrams(const std::vector<std::string> &diagram_names,
clanguml::config::config &config, const std::string &od,
const std::unique_ptr<clang::tooling::CompilationDatabase> &db,
const int verbose, const unsigned int thread_count,
const std::map<std::string, std::vector<std::string>>
&translation_units_map)
{
util::thread_pool_executor generator_executor{thread_count};
std::vector<std::future<void>> futs;
for (const auto &[name, diagram] : config.diagrams) {
// If there are any specific diagram names provided on the command line,
// and this diagram is not in that list - skip it
if (!diagram_names.empty() && !util::contains(diagram_names, name))
continue;
const auto &valid_translation_units = translation_units_map.at(name);
if (valid_translation_units.empty()) {
LOG_ERROR(
"Diagram {} generation failed: no translation units found",
name);
continue;
}
futs.emplace_back(generator_executor.add(
[&od, &name = name, &diagram = diagram, db = std::ref(*db),
translation_units = valid_translation_units, verbose]() {
try {
generate_diagram(
od, name, diagram, db, translation_units, verbose);
}
catch (std::runtime_error &e) {
LOG_ERROR(e.what());
}
}));
}
for (auto &fut : futs) {
fut.get();
}
}
void find_translation_units_for_diagrams(
const std::vector<std::string> &diagram_names,
clanguml::config::config &config,
const std::vector<std::string> &compilation_database_files,
std::map<std::string, std::vector<std::string>> &translation_units_map)
{
const auto current_directory = std::filesystem::current_path();
for (const auto &[name, diagram] : config.diagrams) {
// If there are any specific diagram names provided on the command line,
// and this diagram is not in that list - skip it
if (!diagram_names.empty() && !util::contains(diagram_names, name))
continue;
// If glob is not defined use all translation units from the
// compilation database
if (!diagram->glob.has_value) {
translation_units_map[name] = compilation_database_files;
}
// Otherwise, get all translation units matching the glob from diagram
// configuration
else {
const std::vector<std::string> translation_units =
diagram->get_translation_units(current_directory);
std::vector<std::string> valid_translation_units{};
std::copy_if(compilation_database_files.begin(),
compilation_database_files.end(),
std::back_inserter(valid_translation_units),
[&translation_units](const auto &tu) {
return util::contains(translation_units, tu);
});
translation_units_map[name] = std::move(valid_translation_units);
}
}
}
void generate_diagram(const std::string &od, const std::string &name,
std::shared_ptr<clanguml::config::diagram> diagram,
const clang::tooling::CompilationDatabase &db,
@ -372,6 +291,87 @@ void generate_diagram(const std::string &od, const std::string &name,
ofs.close();
}
void generate_diagrams(const std::vector<std::string> &diagram_names,
clanguml::config::config &config, const std::string &od,
const std::unique_ptr<clang::tooling::CompilationDatabase> &db,
const int verbose, const unsigned int thread_count,
const std::map<std::string, std::vector<std::string>>
&translation_units_map)
{
util::thread_pool_executor generator_executor{thread_count};
std::vector<std::future<void>> futs;
for (const auto &[name, diagram] : config.diagrams) {
// If there are any specific diagram names provided on the command line,
// and this diagram is not in that list - skip it
if (!diagram_names.empty() && !util::contains(diagram_names, name))
continue;
const auto &valid_translation_units = translation_units_map.at(name);
if (valid_translation_units.empty()) {
LOG_ERROR(
"Diagram {} generation failed: no translation units found",
name);
continue;
}
futs.emplace_back(generator_executor.add(
[&od, &name = name, &diagram = diagram, db = std::ref(*db),
translation_units = valid_translation_units, verbose]() {
try {
generate_diagram(
od, name, diagram, db, translation_units, verbose);
}
catch (std::runtime_error &e) {
LOG_ERROR(e.what());
}
}));
}
for (auto &fut : futs) {
fut.get();
}
}
void find_translation_units_for_diagrams(
const std::vector<std::string> &diagram_names,
clanguml::config::config &config,
const std::vector<std::string> &compilation_database_files,
std::map<std::string, std::vector<std::string>> &translation_units_map)
{
const auto current_directory = std::filesystem::current_path();
for (const auto &[name, diagram] : config.diagrams) {
// If there are any specific diagram names provided on the command line,
// and this diagram is not in that list - skip it
if (!diagram_names.empty() && !util::contains(diagram_names, name))
continue;
// If glob is not defined use all translation units from the
// compilation database
if (!diagram->glob.has_value) {
translation_units_map[name] = compilation_database_files;
}
// Otherwise, get all translation units matching the glob from diagram
// configuration
else {
const std::vector<std::string> translation_units =
diagram->get_translation_units(current_directory);
std::vector<std::string> valid_translation_units{};
std::copy_if(compilation_database_files.begin(),
compilation_database_files.end(),
std::back_inserter(valid_translation_units),
[&translation_units](const auto &tu) {
return util::contains(translation_units, tu);
});
translation_units_map[name] = std::move(valid_translation_units);
}
}
}
bool ensure_output_directory_exists(const std::string &dir)
{
namespace fs = std::filesystem;

View File

@ -71,7 +71,7 @@ bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls)
if (source_manager().isInSystemHeader(cls->getSourceRange().getBegin()))
return true;
if (!diagram().should_include(cls->getQualifiedNameAsString()))
if (!should_include(cls))
return true;
if (cls->isTemplated() && cls->getDescribedTemplate()) {
@ -142,10 +142,7 @@ bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls)
bool translation_unit_visitor::VisitClassTemplateDecl(
clang::ClassTemplateDecl *cls)
{
if (source_manager().isInSystemHeader(cls->getSourceRange().getBegin()))
return true;
if (!diagram().should_include(cls->getQualifiedNameAsString()))
if (!should_include(cls))
return true;
LOG_TRACE("Visiting class template declaration {} at {} [{}]",
@ -191,10 +188,7 @@ bool translation_unit_visitor::VisitClassTemplateDecl(
bool translation_unit_visitor::VisitClassTemplateSpecializationDecl(
clang::ClassTemplateSpecializationDecl *cls)
{
if (source_manager().isInSystemHeader(cls->getSourceRange().getBegin()))
return true;
if (!diagram().should_include(cls->getQualifiedNameAsString()))
if (!should_include(cls))
return true;
LOG_TRACE("Visiting template specialization declaration {} at {}",
@ -241,7 +235,7 @@ bool translation_unit_visitor::VisitClassTemplateSpecializationDecl(
bool translation_unit_visitor::VisitCXXMethodDecl(clang::CXXMethodDecl *m)
{
if (!diagram().should_include(m->getParent()->getQualifiedNameAsString()))
if (!should_include(m))
return true;
if (!m->isThisDeclarationADefinition()) {
@ -331,11 +325,11 @@ bool translation_unit_visitor::VisitFunctionDecl(clang::FunctionDecl *f)
if (f->isCXXClassMember())
return true;
const auto function_name = f->getQualifiedNameAsString();
if (!diagram().should_include(function_name))
if (!should_include(f))
return true;
const auto function_name = f->getQualifiedNameAsString();
if (!f->isThisDeclarationADefinition()) {
if (f->getDefinition())
return VisitFunctionDecl(
@ -354,31 +348,13 @@ bool translation_unit_visitor::VisitFunctionDecl(clang::FunctionDecl *f)
}
}
std::unique_ptr<model::function> f_ptr{};
if (f->isFunctionTemplateSpecialization()) {
auto f_ptr = build_function_template_instantiation(*f);
f_ptr->set_id(common::to_id(f_ptr->full_name(false)));
f_ptr->is_void(f->getReturnType()->isVoidType());
context().update(f);
context().set_caller_id(f_ptr->id());
if (f->isThisDeclarationADefinition()) {
set_unique_id(f->getFirstDecl()->getID(), f_ptr->id());
}
set_unique_id(f->getID(), f_ptr->id());
process_comment(*f, *f_ptr);
set_source_location(*f, *f_ptr);
// TODO: Handle overloaded functions with different arguments
diagram().add_participant(std::move(f_ptr));
f_ptr = build_function_template_instantiation(*f);
}
else {
auto f_ptr = std::make_unique<sequence_diagram::model::function>(
f_ptr = std::make_unique<sequence_diagram::model::function>(
config().using_namespace());
common::model::namespace_ ns{function_name};
@ -390,39 +366,38 @@ bool translation_unit_visitor::VisitFunctionDecl(clang::FunctionDecl *f)
f_ptr->add_parameter(simplify_system_template(common::to_string(
param->getType(), f->getASTContext(), false)));
}
f_ptr->set_id(common::to_id(f_ptr->full_name(false)));
f_ptr->is_void(f->getReturnType()->isVoidType());
context().update(f);
context().set_caller_id(f_ptr->id());
if (f->isThisDeclarationADefinition()) {
set_unique_id(f->getFirstDecl()->getID(), f_ptr->id());
}
set_unique_id(f->getID(), f_ptr->id());
process_comment(*f, *f_ptr);
set_source_location(*f, *f_ptr);
// TODO: Handle overloaded functions with different arguments
diagram().add_participant(std::move(f_ptr));
}
f_ptr->set_id(common::to_id(f_ptr->full_name(false)));
f_ptr->is_void(f->getReturnType()->isVoidType());
context().update(f);
context().set_caller_id(f_ptr->id());
if (f->isThisDeclarationADefinition()) {
set_unique_id(f->getFirstDecl()->getID(), f_ptr->id());
}
set_unique_id(f->getID(), f_ptr->id());
process_comment(*f, *f_ptr);
set_source_location(*f, *f_ptr);
diagram().add_participant(std::move(f_ptr));
return true;
}
bool translation_unit_visitor::VisitFunctionTemplateDecl(
clang::FunctionTemplateDecl *function_template)
{
const auto function_name = function_template->getQualifiedNameAsString();
if (!diagram().should_include(function_name))
if (!should_include(function_template))
return true;
const auto function_name = function_template->getQualifiedNameAsString();
LOG_TRACE("Visiting function template declaration {} at {}", function_name,
function_template->getLocation().printToString(source_manager()));
@ -442,15 +417,15 @@ bool translation_unit_visitor::VisitFunctionTemplateDecl(
param->getType(), function_template->getASTContext(), false)));
}
process_comment(*function_template, *f_ptr);
set_source_location(*function_template, *f_ptr);
f_ptr->set_id(common::to_id(f_ptr->full_name(false)));
f_ptr->is_void(
function_template->getAsFunction()->getReturnType()->isVoidType());
process_comment(*function_template, *f_ptr);
set_source_location(*function_template, *f_ptr);
context().update(function_template);
context().set_caller_id(f_ptr->id());
@ -464,6 +439,9 @@ bool translation_unit_visitor::VisitFunctionTemplateDecl(
bool translation_unit_visitor::VisitLambdaExpr(clang::LambdaExpr *expr)
{
if (!should_include(expr))
return true;
const auto lambda_full_name =
expr->getLambdaClass()->getCanonicalDecl()->getNameAsString();
@ -868,20 +846,7 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
using clanguml::sequence_diagram::model::activity;
using clanguml::sequence_diagram::model::message;
if (context().caller_id() == 0)
return true;
// Skip casts, moves and such
if (expr->isCallToStdMove())
return true;
if (expr->isImplicitCXXThis())
return true;
if (clang::dyn_cast_or_null<clang::ImplicitCastExpr>(expr))
return true;
if (!context().valid())
if (!should_include(expr))
return true;
LOG_TRACE("Visiting call expression at {} [caller_id = {}]",
@ -1040,12 +1005,10 @@ bool translation_unit_visitor::process_class_method_call_expression(
auto *callee_decl = method_decl ? method_decl->getParent() : nullptr;
if (!(callee_decl &&
diagram().should_include(callee_decl->getQualifiedNameAsString())))
if (!callee_decl)
return false;
if (!diagram().should_include(
common::access_specifier_to_access_t(method_decl->getAccess())))
if (!should_include(callee_decl) || !should_include(method_decl))
return false;
m.set_to(method_decl->getID());
@ -1151,9 +1114,7 @@ bool translation_unit_visitor::process_function_call_expression(
if (!callee_function)
return false;
auto callee_name = callee_function->getQualifiedNameAsString() + "()";
if (!diagram().should_include(callee_name))
if (!should_include(callee_function))
return false;
// Skip free functions declared in files outside of included paths
@ -1161,6 +1122,8 @@ bool translation_unit_visitor::process_function_call_expression(
!diagram().should_include(common::model::source_file{m.file()}))
return false;
auto callee_name = callee_function->getQualifiedNameAsString() + "()";
std::unique_ptr<model::function_template> f_ptr;
if (!get_unique_id(callee_function->getID()).has_value()) {
@ -2163,4 +2126,90 @@ void translation_unit_visitor::finalize()
}
}
}
bool translation_unit_visitor::should_include(const clang::TagDecl *decl) const
{
if (source_manager().isInSystemHeader(decl->getSourceRange().getBegin()))
return false;
const auto decl_file = decl->getLocation().printToString(source_manager());
return diagram().should_include(decl->getQualifiedNameAsString()) &&
diagram().should_include(common::model::source_file{decl_file});
}
bool translation_unit_visitor::should_include(
const clang::LambdaExpr *expr) const
{
const auto expr_file = expr->getBeginLoc().printToString(source_manager());
return diagram().should_include(common::model::source_file{expr_file});
}
bool translation_unit_visitor::should_include(const clang::CallExpr *expr) const
{
if (context().caller_id() == 0)
return false;
// Skip casts, moves and such
if (expr->isCallToStdMove())
return false;
if (expr->isImplicitCXXThis())
return false;
if (clang::dyn_cast_or_null<clang::ImplicitCastExpr>(expr))
return false;
if (!context().valid())
return false;
const auto expr_file = expr->getBeginLoc().printToString(source_manager());
if (!diagram().should_include(common::model::source_file{expr_file}))
return false;
return true;
}
bool translation_unit_visitor::should_include(
const clang::CXXMethodDecl *decl) const
{
if (!should_include(decl->getParent()))
return false;
if (!diagram().should_include(
common::access_specifier_to_access_t(decl->getAccess())))
return false;
LOG_DBG("Including method {}", decl->getQualifiedNameAsString());
return true;
}
bool translation_unit_visitor::should_include(
const clang::FunctionDecl *decl) const
{
const auto decl_file = decl->getLocation().printToString(source_manager());
return diagram().should_include(decl->getQualifiedNameAsString()) &&
diagram().should_include(common::model::source_file{decl_file});
}
bool translation_unit_visitor::should_include(
const clang::FunctionTemplateDecl *decl) const
{
return should_include(decl->getAsFunction());
}
bool translation_unit_visitor::should_include(
const clang::ClassTemplateDecl *decl) const
{
if (source_manager().isInSystemHeader(decl->getSourceRange().getBegin()))
return false;
const auto decl_file = decl->getLocation().printToString(source_manager());
return diagram().should_include(decl->getQualifiedNameAsString()) &&
diagram().should_include(common::model::source_file{decl_file});
}
}

View File

@ -163,6 +163,14 @@ public:
int64_t local_id) const;
private:
bool should_include(const clang::TagDecl *decl) const;
bool should_include(const clang::LambdaExpr *expr) const;
bool should_include(const clang::CallExpr *expr) const;
bool should_include(const clang::CXXMethodDecl *decl) const;
bool should_include(const clang::FunctionDecl *decl) const;
bool should_include(const clang::FunctionTemplateDecl *decl) const;
bool should_include(const clang::ClassTemplateDecl *decl) const;
std::unique_ptr<clanguml::sequence_diagram::model::class_>
create_class_declaration(clang::CXXRecordDecl *cls);

View File

@ -10,9 +10,8 @@ diagrams:
include:
namespaces:
- clanguml::t20017
# This only affects free function participants
paths:
- t20017.cc
- .
using_namespace:
- clanguml::t20017
start_from:

View File

@ -1,30 +1,28 @@
type: sequence
# Group free functions into one participant per file
combine_free_functions_into_file_participants: true
# Do not generate method or function arguments
generate_method_arguments: none
# Parse only 1 translation unit for this diagram
glob:
- src/main.cc
include:
# Only include entities and call expressions from the project directory
paths:
- src
exclude:
namespaces:
- std
- fmt
- spdlog
- clang
- llvm
- nlohmann
- CLI
- __gnu_cxx
- inja
# Exclude calls to config options
elements:
- clanguml::config::option<std::string>
# Exclude entities and call expressions from irrelevant files
paths:
- src/util/util.h
- src/common/model/enums.h
using_namespace:
- clanguml
plantuml:
before:
- 'title clang-uml main function sequence diagram'
# Use clang-uml main function as entry point for this diagram
start_from:
- function: main(int,const char **)