From 1c7e64c51e99162ca59f86adaa860d28efb615dd Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Sun, 18 Dec 2022 22:18:26 +0100 Subject: [PATCH] Added should_include helper methods in sequence diagram visitor --- src/common/model/diagram_filter.cc | 7 +- src/main.cc | 162 ++++++------- .../visitor/translation_unit_visitor.cc | 215 +++++++++++------- .../visitor/translation_unit_visitor.h | 8 + tests/t20017/.clang-uml | 3 +- uml/main_sequence_diagram.yml | 18 +- 6 files changed, 235 insertions(+), 178 deletions(-) diff --git a/src/common/model/diagram_filter.cc b/src/common/model/diagram_filter.cc index 8d515ac5..f87fcdf2 100644 --- a/src/common/model/diagram_filter.cc +++ b/src/common/model/diagram_filter.cc @@ -209,8 +209,11 @@ element_filter::element_filter(filter_t type, std::vector 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 roots) diff --git a/src/main.cc b/src/main.cc index efe97439..e922200d 100644 --- a/src/main.cc +++ b/src/main.cc @@ -209,87 +209,6 @@ int main(int argc, const char *argv[]) return 0; } -void generate_diagrams(const std::vector &diagram_names, - clanguml::config::config &config, const std::string &od, - const std::unique_ptr &db, - const int verbose, const unsigned int thread_count, - const std::map> - &translation_units_map) -{ - util::thread_pool_executor generator_executor{thread_count}; - std::vector> 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 &diagram_names, - clanguml::config::config &config, - const std::vector &compilation_database_files, - std::map> &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 translation_units = - diagram->get_translation_units(current_directory); - - std::vector 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 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 &diagram_names, + clanguml::config::config &config, const std::string &od, + const std::unique_ptr &db, + const int verbose, const unsigned int thread_count, + const std::map> + &translation_units_map) +{ + util::thread_pool_executor generator_executor{thread_count}; + std::vector> 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 &diagram_names, + clanguml::config::config &config, + const std::vector &compilation_database_files, + std::map> &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 translation_units = + diagram->get_translation_units(current_directory); + + std::vector 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; diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index 13d3b9a8..2c333632 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -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 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( + f_ptr = std::make_unique( 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(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 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(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}); +} } diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.h b/src/sequence_diagram/visitor/translation_unit_visitor.h index 8fb09c45..8d44a1f8 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.h +++ b/src/sequence_diagram/visitor/translation_unit_visitor.h @@ -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 create_class_declaration(clang::CXXRecordDecl *cls); diff --git a/tests/t20017/.clang-uml b/tests/t20017/.clang-uml index a5a65e48..860e217d 100644 --- a/tests/t20017/.clang-uml +++ b/tests/t20017/.clang-uml @@ -10,9 +10,8 @@ diagrams: include: namespaces: - clanguml::t20017 - # This only affects free function participants paths: - - t20017.cc + - . using_namespace: - clanguml::t20017 start_from: diff --git a/uml/main_sequence_diagram.yml b/uml/main_sequence_diagram.yml index dd51132b..88ddf203 100644 --- a/uml/main_sequence_diagram.yml +++ b/uml/main_sequence_diagram.yml @@ -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 + # 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 **) \ No newline at end of file