From 97719e46fc3cc9ec40ddbc9457e0aee3e3254205 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Thu, 11 Jan 2024 11:26:41 +0100 Subject: [PATCH] Enabled type_aliases config option for sequence diagrams (#224) --- CHANGELOG.md | 3 +- src/config/config.cc | 1 + src/config/config.h | 3 +- src/config/option.h | 16 ++++- src/config/schema.h | 2 + src/config/yaml_decoders.cc | 2 + .../visitor/translation_unit_visitor.cc | 16 ++--- .../visitor/translation_unit_visitor.h | 2 +- tests/t20039/.clang-uml | 14 +++++ tests/t20039/t20039.cc | 34 ++++++++++ tests/t20039/test_case.h | 63 +++++++++++++++++++ tests/test_cases.cc | 1 + tests/test_cases.yaml | 3 + tests/test_config.cc | 31 +++++++++ tests/test_config_data/type_aliases.yml | 17 +++++ 15 files changed, 195 insertions(+), 13 deletions(-) create mode 100644 tests/t20039/.clang-uml create mode 100644 tests/t20039/t20039.cc create mode 100644 tests/t20039/test_case.h create mode 100644 tests/test_config_data/type_aliases.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 2564d0ad..85b2f67c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # CHANGELOG - + + * Enabled type_aliases config option for sequence diagrams (#224) * Refactored and unified JSON generators output (#223) * Added support for C++20 module based packages in class diagrams (#101) * Added support for class diagram filtering based on C++20 modules (#195) diff --git a/src/config/config.cc b/src/config/config.cc index d875908c..829c1361 100644 --- a/src/config/config.cc +++ b/src/config/config.cc @@ -217,6 +217,7 @@ void inheritable_diagram_options::inherit( parent.generate_condition_statements); debug_mode.override(parent.debug_mode); generate_metadata.override(parent.generate_metadata); + type_aliases.override(parent.type_aliases); } std::string inheritable_diagram_options::simplify_template_type( diff --git a/src/config/config.h b/src/config/config.h index 0db27a25..32944fdd 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -538,7 +538,8 @@ struct inheritable_diagram_options { option base_directory{"__parent_path"}; option generate_system_headers{"generate_system_headers", false}; option relationship_hints{"relationship_hints"}; - option type_aliases{"type_aliases"}; + option type_aliases{ + "type_aliases", option_inherit_mode::kAppend}; option comment_parser{ "comment_parser", comment_parser_t::plain}; option combine_free_functions_into_file_participants{ diff --git a/src/config/option.h b/src/config/option.h index 0f446d59..699e3e59 100644 --- a/src/config/option.h +++ b/src/config/option.h @@ -25,6 +25,18 @@ namespace config { template void append_value(T &l, const T &r) { l = r; } +template +void append_value(std::vector &l, const std::vector &r) +{ + l.insert(std::end(l), r.begin(), r.end()); +} + +template +void append_value(std::map &l, const std::map &r) +{ + l.insert(r.begin(), r.end()); +} + /** * Possible option inheritance methods from top level to diagram level. */ @@ -102,9 +114,7 @@ template struct option { has_value = true; } else if (!is_declared && o.is_declared) { - value = o.value; - is_declared = true; - has_value = true; + set(o.value); } } diff --git a/src/config/schema.h b/src/config/schema.h index 63f52bce..b1e05ad7 100644 --- a/src/config/schema.h +++ b/src/config/schema.h @@ -205,6 +205,7 @@ types: after: !optional [string] cmd: !optional string relative_to: !optional string + type_aliases: !optional map_t using_namespace: !optional [string, [string]] generate_metadata: !optional bool title: !optional string @@ -342,6 +343,7 @@ root: package_type: !optional package_type_t generate_template_argument_dependencies: !optional bool skip_redundant_dependencies: !optional bool + type_aliases: !optional map_t )"; } // namespace clanguml::config \ No newline at end of file diff --git a/src/config/yaml_decoders.cc b/src/config/yaml_decoders.cc index 0c414c4d..691a7c17 100644 --- a/src/config/yaml_decoders.cc +++ b/src/config/yaml_decoders.cc @@ -663,6 +663,7 @@ template <> struct convert { get_option(node, rhs.generate_method_arguments); get_option(node, rhs.generate_message_comments); get_option(node, rhs.message_comment_width); + get_option(node, rhs.type_aliases); get_option(node, rhs.get_relative_to()); @@ -836,6 +837,7 @@ template <> struct convert { get_option(node, rhs.generate_condition_statements); get_option(node, rhs.generate_message_comments); get_option(node, rhs.message_comment_width); + get_option(node, rhs.type_aliases); rhs.base_directory.set(node["__parent_path"].as()); get_option(node, rhs.get_relative_to()); diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index 5a79a309..5b02b279 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -114,8 +114,8 @@ bool translation_unit_visitor::VisitCXXRecordDecl( forward_declarations_.erase(class_id); if (diagram().should_include(class_model)) { - LOG_DBG("Adding class {} with id {}", class_model.full_name(false), - class_model.id()); + LOG_DBG("Adding class participant {} with id {}", + class_model.full_name(false), class_model.id()); assert(class_model.id() == class_id); @@ -166,7 +166,8 @@ bool translation_unit_visitor::VisitClassTemplateDecl( forward_declarations_.erase(id); if (diagram().should_include(*class_model_ptr)) { - LOG_DBG("Adding class template {} with id {}", class_full_name, id); + LOG_DBG("Adding class template participant {} with id {}", + class_full_name, id); context().set_caller_id(id); context().update(declaration); @@ -212,7 +213,8 @@ bool translation_unit_visitor::VisitClassTemplateSpecializationDecl( forward_declarations_.erase(id); if (diagram().should_include(*template_specialization_ptr)) { - LOG_DBG("Adding class template specialization {} with id {}", + LOG_DBG( + "Adding class template specialization participant {} with id {}", class_full_name, id); context().set_caller_id(id); @@ -2047,12 +2049,12 @@ void translation_unit_visitor::process_template_specialization_argument( argument.set_type(type_name); } - LOG_TRACE("Adding template instantiation argument {}", - argument.to_string(config().using_namespace(), false)); - simplify_system_template( argument, argument.to_string(config().using_namespace(), false)); + LOG_TRACE("Adding template instantiation argument {}", + argument.to_string(config().using_namespace(), false)); + template_instantiation.add_template(std::move(argument)); } else if (argument_kind == clang::TemplateArgument::Integral) { diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.h b/src/sequence_diagram/visitor/translation_unit_visitor.h index ddf10ab9..d1190096 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.h +++ b/src/sequence_diagram/visitor/translation_unit_visitor.h @@ -312,7 +312,7 @@ private: bool should_include(const clang::ClassTemplateDecl *decl) const; /** - * @todo Refactor this group of methods to @ref template_builder + * @todo #227 Refactor this group of methods to @ref template_builder * * @{ */ diff --git a/tests/t20039/.clang-uml b/tests/t20039/.clang-uml new file mode 100644 index 00000000..ff038945 --- /dev/null +++ b/tests/t20039/.clang-uml @@ -0,0 +1,14 @@ +diagrams: + t20039_sequence: + type: sequence + glob: + - t20039.cc + include: + namespaces: + - clanguml::t20039 + using_namespace: clanguml::t20039 + type_aliases: + "std::vector": int_vec_t + "std::map": int_map_t + from: + - function: "clanguml::t20039::tmain()" \ No newline at end of file diff --git a/tests/t20039/t20039.cc b/tests/t20039/t20039.cc new file mode 100644 index 00000000..62804d36 --- /dev/null +++ b/tests/t20039/t20039.cc @@ -0,0 +1,34 @@ +#include +#include +#include + +namespace clanguml { +namespace t20039 { + +template struct A { + std::vector> a(T p) { return {}; } +}; + +struct R { + A a_int; + A> a_intvec; + A> a_intmap; + + void run() + { + a_int.a({}); + a_intvec.a({}); + a_intmap.a({}); + } +}; + +int tmain() +{ + R r; + + r.run(); + + return 0; +} +} +} \ No newline at end of file diff --git a/tests/t20039/test_case.h b/tests/t20039/test_case.h new file mode 100644 index 00000000..5c41852d --- /dev/null +++ b/tests/t20039/test_case.h @@ -0,0 +1,63 @@ +/** + * tests/t20039/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("t20039", "[test-case][sequence]") +{ + auto [config, db] = load_config("t20039"); + + auto diagram = config.diagrams["t20039_sequence"]; + + REQUIRE(diagram->name == "t20039_sequence"); + + auto model = generate_sequence_diagram(*db, diagram); + + REQUIRE(model->name() == "t20039_sequence"); + + { + auto src = generate_sequence_puml(diagram, *model); + AliasMatcher _A(src); + + REQUIRE_THAT(src, StartsWith("@startuml")); + REQUIRE_THAT(src, EndsWith("@enduml\n")); + + // Check if all calls exist + REQUIRE_THAT(src, HasCall(_A("tmain()"), _A("R"), "run()")); + REQUIRE_THAT(src, HasCall(_A("R"), _A("A"), "a(int)")); + REQUIRE_THAT(src, HasCall(_A("R"), _A("A"), "a(int_vec_t)")); + REQUIRE_THAT(src, HasCall(_A("R"), _A("A"), "a(int_map_t)")); + + save_puml(config.output_directory(), diagram->name + ".puml", src); + } + + { + auto j = generate_sequence_json(diagram, *model); + + using namespace json; + + save_json(config.output_directory(), diagram->name + ".json", j); + } + + { + auto src = generate_sequence_mermaid(diagram, *model); + + mermaid::AliasMatcher _A(src); + using mermaid::IsClass; + + save_mermaid(config.output_directory(), diagram->name + ".mmd", src); + } +} \ No newline at end of file diff --git a/tests/test_cases.cc b/tests/test_cases.cc index e49cc89d..260730d5 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -458,6 +458,7 @@ using namespace clanguml::test::matchers; #include "t20036/test_case.h" #include "t20037/test_case.h" #include "t20038/test_case.h" +#include "t20039/test_case.h" /// /// Package diagram tests diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index 544070c7..5bc0fba0 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -328,6 +328,9 @@ test_cases: - name: t20038 title: Sequence diagram comment decorator test case description: + - name: t20039 + title: Test case for type aliases config option in sequence diagrams + description: Package diagrams: - name: t30001 title: Basic package diagram test case diff --git a/tests/test_config.cc b/tests/test_config.cc index 7008b690..c31872c2 100644 --- a/tests/test_config.cc +++ b/tests/test_config.cc @@ -410,6 +410,37 @@ TEST_CASE("Test config full clang uml dump", "[unit-test]") CHECK(cfg.diagrams.size() == 32); } +TEST_CASE("Test config type aliases", "[unit-test]") +{ + auto cfg = clanguml::config::load("./test_config_data/type_aliases.yml"); + + CHECK(cfg.diagrams.size() == 2); + auto &def = *cfg.diagrams["class_diagram"]; + CHECK( + def.simplify_template_type( + "ns1::ns2::container") == "custom_map_t"); + CHECK(def.simplify_template_type( + "ns1::ns2::container") == + "string_map_t"); + CHECK( + def.simplify_template_type("std::basic_string") == "std::string"); + CHECK(def.simplify_template_type("std::basic_string") == + "unicode_t"); + + def = *cfg.diagrams["sequence_diagram"]; + CHECK( + def.simplify_template_type( + "ns1::ns2::container") == "custom_map_t"); + CHECK(def.simplify_template_type( + "ns1::ns2::Object::iterator " + "*,std::vector,std::allocator>>>") == "ObjectPtrIt"); + CHECK( + def.simplify_template_type("std::basic_string") == "std::string"); + CHECK(def.simplify_template_type("std::vector>") == + "std::vector"); +} + /// /// Main test function /// diff --git a/tests/test_config_data/type_aliases.yml b/tests/test_config_data/type_aliases.yml new file mode 100644 index 00000000..75775618 --- /dev/null +++ b/tests/test_config_data/type_aliases.yml @@ -0,0 +1,17 @@ +type_aliases: + "ns1::ns2::container": custom_map_t +diagrams: + class_diagram: + type: class + glob: + - test.cc + type_aliases: + "ns1::ns2::container": string_map_t + "std::basic_string": unicode_t + sequence_diagram: + type: class + relative_to: . + glob: + - test.cc + type_aliases: + "ns1::ns2::Object::iterator *,std::vector,std::allocator>>>": "ObjectPtrIt"