diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index f474eed4..3aaf03a3 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -76,7 +76,8 @@ bool translation_unit_visitor::VisitNamespaceDecl(clang::NamespaceDecl *ns) p->set_id(common::to_id(*ns)); id_mapper().add(ns->getID(), p->id()); - if (diagram().should_include(*p) && !diagram().get(p->id())) { + if (config().filter_mode() == config::filter_mode_t::advanced || + (diagram().should_include(*p) && !diagram().get(p->id()))) { process_comment(*ns, *p); set_source_location(*ns, *p); @@ -667,10 +668,6 @@ void translation_unit_visitor::process_concept_specialization_relationships( bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls) { - // Skip system headers - if (source_manager().isInSystemHeader(cls->getSourceRange().getBegin())) - return true; - if (!should_include(cls)) return true; diff --git a/src/common/model/filters/diagram_filter_factory.cc b/src/common/model/filters/diagram_filter_factory.cc index 8b11283d..3988a8cd 100644 --- a/src/common/model/filters/diagram_filter_factory.cc +++ b/src/common/model/filters/diagram_filter_factory.cc @@ -249,6 +249,25 @@ void basic_diagram_filter_initializer::initialize() } } +template <> +void advanced_diagram_filter_initializer::add_filter< + source_file_dependency_filter_t>(const filter_t &filter_type, + const std::vector &filter_config, + std::vector> &result, relationship_t &&rt, + bool &&direction) +{ + std::vector deps; + for (auto &&path : filter_config) { + if (auto p = path.get(); p.has_value()) { + const std::filesystem::path dep_path{*p}; + deps.emplace_back(dep_path.lexically_normal().string()); + } + } + + result.emplace_back(std::make_unique( + filter_type, deps, rt, direction)); +} + std::vector> advanced_diagram_filter_initializer::build( filter_t filter_type, const config::filter &filter_config) diff --git a/src/common/model/filters/diagram_filter_factory.h b/src/common/model/filters/diagram_filter_factory.h index 8c13eb4a..cedd70a3 100644 --- a/src/common/model/filters/diagram_filter_factory.h +++ b/src/common/model/filters/diagram_filter_factory.h @@ -84,27 +84,15 @@ private: result.emplace_back(std::make_unique( filter_type, filter_config, std::forward(args)...)); } - - template <> - void add_filter( - const filter_t &filter_type, - const std::vector &filter_config, - std::vector> &result, - relationship_t &&rt, bool &&direction) - { - std::vector deps; - for (auto &&path : filter_config) { - if (auto p = path.get(); p.has_value()) { - const std::filesystem::path dep_path{*p}; - deps.emplace_back(dep_path.lexically_normal().string()); - } - } - - result.emplace_back(std::make_unique( - filter_type, deps, rt, direction)); - } }; +template <> +void advanced_diagram_filter_initializer::add_filter< + source_file_dependency_filter_t>(const filter_t &filter_type, + const std::vector &filter_config, + std::vector> &result, relationship_t &&rt, + bool &&direction); + class diagram_filter_factory { public: static std::unique_ptr create( diff --git a/src/common/visitor/translation_unit_visitor.h b/src/common/visitor/translation_unit_visitor.h index 9593ba8f..f5f88444 100644 --- a/src/common/visitor/translation_unit_visitor.h +++ b/src/common/visitor/translation_unit_visitor.h @@ -290,6 +290,13 @@ public: return stripped_comment; } + bool skip_system_header_decl(const clang::NamedDecl *decl) + { + return !config().include_system_headers() && + source_manager().isInSystemHeader( + decl->getSourceRange().getBegin()); + } + /** * @brief Check if the diagram should include a declaration. * @@ -301,10 +308,12 @@ public: if (decl == nullptr) return false; - if (source_manager().isInSystemHeader( - decl->getSourceRange().getBegin())) + if (skip_system_header_decl(decl)) return false; + if (config().filter_mode() == config::filter_mode_t::advanced) + return true; + auto should_include_namespace = diagram().should_include( common::model::namespace_{decl->getQualifiedNameAsString()}); diff --git a/src/config/config.cc b/src/config/config.cc index 18d20908..d0520bc8 100644 --- a/src/config/config.cc +++ b/src/config/config.cc @@ -242,6 +242,7 @@ void inheritable_diagram_options::inherit( include_relations_also_as_members.override( parent.include_relations_also_as_members); filter_mode.override(parent.filter_mode); + include_system_headers.override(parent.include_system_headers); include.override(parent.include); exclude.override(parent.exclude); puml.override(parent.puml); diff --git a/src/config/config.h b/src/config/config.h index 843d448c..d9b40de6 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -555,6 +555,7 @@ struct inheritable_diagram_options { option include_relations_also_as_members{ "include_relations_also_as_members", true}; option filter_mode{"filter_mode", filter_mode_t::basic}; + option include_system_headers{"include_system_headers", false}; option include{"include"}; option exclude{"exclude"}; option puml{"plantuml", option_inherit_mode::kAppend}; diff --git a/src/config/schema.h b/src/config/schema.h index 6b8c597a..ebaa3d47 100644 --- a/src/config/schema.h +++ b/src/config/schema.h @@ -177,6 +177,7 @@ types: comment_parser: !optional comment_parser_t debug_mode: !optional bool filter_mode: !optional filter_mode_t + include_system_headers: !optional bool exclude: !optional filter_t generate_links: !optional generate_links_t git: !optional git_t @@ -221,6 +222,8 @@ types: debug_mode: !optional bool exclude: !optional filter_t generate_links: !optional generate_links_t + filter_mode: !optional filter_mode_t + include_system_headers: !optional bool git: !optional git_t glob: !optional [string] include: !optional filter_t @@ -264,6 +267,8 @@ types: generate_links: !optional generate_links_t git: !optional git_t glob: !optional [string] + filter_mode: !optional filter_mode_t + include_system_headers: !optional bool include: !optional filter_t plantuml: !optional before: !optional [string] @@ -292,6 +297,8 @@ types: __parent_path: !optional string comment_parser: !optional comment_parser_t debug_mode: !optional bool + filter_mode: !optional filter_mode_t + include_system_headers: !optional bool exclude: !optional filter_t generate_links: !optional generate_links_t git: !optional git_t @@ -377,6 +384,8 @@ root: generate_template_argument_dependencies: !optional bool skip_redundant_dependencies: !optional bool type_aliases: !optional map_t + filter_mode: !optional filter_mode_t + include_system_headers: !optional bool )"; } // namespace clanguml::config \ No newline at end of file diff --git a/src/config/yaml_decoders.cc b/src/config/yaml_decoders.cc index 666854a6..f4994b2a 100644 --- a/src/config/yaml_decoders.cc +++ b/src/config/yaml_decoders.cc @@ -541,7 +541,7 @@ template <> struct convert { } if (node["allof"]) { - rhs.anyof = std::make_unique(node["anyof"].as()); + rhs.allof = std::make_unique(node["allof"].as()); } if (node["namespaces"]) { @@ -655,6 +655,7 @@ template bool decode_diagram(const Node &node, T &rhs) get_option(node, rhs.using_namespace); get_option(node, rhs.using_module); get_option(node, rhs.filter_mode); + get_option(node, rhs.include_system_headers); get_option(node, rhs.include); get_option(node, rhs.exclude); get_option(node, rhs.puml); diff --git a/tests/t00080/.clang-uml b/tests/t00080/.clang-uml new file mode 100644 index 00000000..e3951d40 --- /dev/null +++ b/tests/t00080/.clang-uml @@ -0,0 +1,14 @@ +diagrams: + t00080_class: + type: class + filter_mode: advanced + include_system_headers: true + glob: + - t00080.cc + include: + anyof: + namespaces: + - clanguml::t00080 + elements: + - std::thread + using_namespace: clanguml::t00080 \ No newline at end of file diff --git a/tests/t00080/t00080.cc b/tests/t00080/t00080.cc new file mode 100644 index 00000000..c3016e72 --- /dev/null +++ b/tests/t00080/t00080.cc @@ -0,0 +1,24 @@ +#include + +namespace clanguml { +namespace t00080 { + +class Worker : public std::thread { +public: + using std::thread::thread; + + ~Worker() + { + if (this->joinable()) { + this->join(); + } + } + + void start(int delay) { } +}; + +struct R { + Worker *w; +}; +} +} \ No newline at end of file diff --git a/tests/t00080/test_case.h b/tests/t00080/test_case.h new file mode 100644 index 00000000..2cb48383 --- /dev/null +++ b/tests/t00080/test_case.h @@ -0,0 +1,35 @@ +/** + * tests/t00080/test_case.h + * + * Copyright (c) 2021-2024 Bartek Kryza + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +TEST_CASE("t00080") +{ + using namespace clanguml::test; + using namespace std::string_literals; + + auto [config, db, diagram, model] = + CHECK_CLASS_MODEL("t00080", "t00080_class"); + + CHECK_CLASS_DIAGRAM(*config, diagram, *model, [](const auto &src) { + REQUIRE(IsClass(src, "Worker")); + REQUIRE(IsClass(src, "R")); + REQUIRE(IsClass(src, "std::thread")); + REQUIRE(!IsClass(src, "std::jthread")); + + REQUIRE(IsAssociation(src, "R", "Worker", "w")); + }); +} \ No newline at end of file diff --git a/tests/test_cases.cc b/tests/test_cases.cc index 1f08d120..a5504aaa 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -553,6 +553,7 @@ void CHECK_INCLUDE_DIAGRAM(const clanguml::config::config &config, #include "t00077/test_case.h" #include "t00078/test_case.h" #include "t00079/test_case.h" +#include "t00080/test_case.h" /// /// Sequence diagram tests diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index fd624f3b..36cd57fb 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -234,6 +234,9 @@ test_cases: - name: t00079 title: Test case for context diagram exclude filter with relationships option description: + - name: t00080 + title: Test case for including elements from system headers + description: Sequence diagrams: - name: t20001 title: Basic sequence diagram test case diff --git a/tests/test_config_data/filters_advanced.yml b/tests/test_config_data/filters_advanced.yml index cdbc55ee..1990beac 100644 --- a/tests/test_config_data/filters_advanced.yml +++ b/tests/test_config_data/filters_advanced.yml @@ -1,5 +1,6 @@ compilation_database_dir: debug output_directory: output +filter_mode: advanced diagrams: anyof_test: type: class @@ -7,7 +8,6 @@ diagrams: glob: - src/**/*.cc - src/**/*.h - filter_mode: advanced include: anyof: namespaces: @@ -17,4 +17,12 @@ diagrams: exclude: anyof: namespaces: - - ns1::ns2::detail \ No newline at end of file + - ns1::ns2::detail + modules_test: + type: class + include: + anyof: + modules: + - mod1::mod2 + namespaces: + - ns1::ns2 \ No newline at end of file diff --git a/tests/test_filters_advanced.cc b/tests/test_filters_advanced.cc index 992576cd..acdcf6f9 100644 --- a/tests/test_filters_advanced.cc +++ b/tests/test_filters_advanced.cc @@ -28,15 +28,14 @@ #include "sequence_diagram/model/diagram.h" #include +using clanguml::common::model::diagram_filter; +using clanguml::common::model::diagram_filter_factory; +using clanguml::common::model::namespace_; +using clanguml::common::model::source_file; +using clanguml::config::filter_mode_t; TEST_CASE("Test advanced diagram filter anyof") { - using clanguml::common::model::diagram_filter; - using clanguml::common::model::diagram_filter_factory; - using clanguml::common::model::namespace_; - using clanguml::common::model::source_file; - using clanguml::config::filter_mode_t; - auto cfg = clanguml::config::load("./test_config_data/filters_advanced.yml"); @@ -61,6 +60,39 @@ TEST_CASE("Test advanced diagram filter anyof") CHECK_FALSE(filter.should_include(namespace_{"ns1::ns2::detail"})); } +TEST_CASE("Test advanced diagram filter modules") +{ + auto cfg = + clanguml::config::load("./test_config_data/filters_advanced.yml"); + + auto &config = *cfg.diagrams["modules_test"]; + clanguml::include_diagram::model::diagram diagram; + + auto filter_ptr = diagram_filter_factory::create(diagram, config); + diagram_filter &filter = *filter_ptr; + + CHECK(config.filter_mode() == filter_mode_t::advanced); + CHECK(filter.should_include(namespace_{"ns1::ns2"})); + CHECK_FALSE(filter.should_include(namespace_{"std::string"})); + + clanguml::common::model::element std_string{{}}; + std_string.set_namespace(namespace_{"std"}); + std_string.set_name("string"); + + CHECK_FALSE(filter.should_include(std_string)); + + CHECK(filter.should_include(namespace_{"ns1"})); + + clanguml::common::model::element e1{{}}; + e1.set_module("mod1::mod2"); + e1.set_namespace(namespace_{"ns5::ns6"}); + e1.set_name("ClassA"); + CHECK(filter.should_include(e1)); + + e1.set_module("mod1::mod3"); + CHECK_FALSE(filter.should_include(e1)); +} + /// /// Main test function ///