mirror of
https://github.com/bkryza/clang-uml.git
synced 2025-05-18 20:00:19 +08:00
Added should_include helper methods in sequence diagram visitor
This commit is contained in:
parent
c4d8bddf96
commit
1c7e64c51e
@ -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)
|
||||
|
162
src/main.cc
162
src/main.cc
@ -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;
|
||||
|
@ -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,6 +366,7 @@ 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)));
|
||||
|
||||
@ -408,9 +385,7 @@ bool translation_unit_visitor::VisitFunctionDecl(clang::FunctionDecl *f)
|
||||
|
||||
set_source_location(*f, *f_ptr);
|
||||
|
||||
// TODO: Handle overloaded functions with different arguments
|
||||
diagram().add_participant(std::move(f_ptr));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -418,11 +393,11 @@ bool translation_unit_visitor::VisitFunctionDecl(clang::FunctionDecl *f)
|
||||
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});
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -10,9 +10,8 @@ diagrams:
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::t20017
|
||||
# This only affects free function participants
|
||||
paths:
|
||||
- t20017.cc
|
||||
- .
|
||||
using_namespace:
|
||||
- clanguml::t20017
|
||||
start_from:
|
||||
|
@ -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 **)
|
Loading…
x
Reference in New Issue
Block a user