From c4ec8bef8a7287a703a1ed7d981e199a7e0618f9 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Tue, 27 Feb 2024 22:36:09 +0100 Subject: [PATCH] Added generate_concept_requirements config option (#237) --- docs/configuration_file.md | 1 + .../mermaid/class_diagram_generator.cc | 4 +- .../plantuml/class_diagram_generator.cc | 4 +- src/config/config.cc | 2 + src/config/config.h | 2 + src/config/schema.h | 2 + src/config/yaml_decoders.cc | 2 + src/config/yaml_emitters.cc | 1 + tests/t00074/.clang-uml | 10 ++ tests/t00074/t00074.cc | 16 ++++ tests/t00074/test_case.h | 91 +++++++++++++++++++ tests/test_cases.cc | 3 + tests/test_cases.yaml | 3 + 13 files changed, 137 insertions(+), 4 deletions(-) create mode 100644 tests/t00074/.clang-uml create mode 100644 tests/t00074/t00074.cc create mode 100644 tests/t00074/test_case.h diff --git a/docs/configuration_file.md b/docs/configuration_file.md index 77b39b64..657a3c23 100644 --- a/docs/configuration_file.md +++ b/docs/configuration_file.md @@ -25,6 +25,7 @@ * `glob` - list of glob patterns to match source code files for analysis * `include_relations_also_as_members` - when set to `false`, class members for relationships are rendered in UML are skipped from class definition (default: `true`) * `generate_method_arguments` - determines whether the class diagrams methods contain full arguments (`full`), are abbreviated (`abbreviated`) or skipped (`none`) +* `generate_concept_requirements` - determines whether concept requirements are rendered in the diagram (default: `true`) * `using_namespace` - similar to C++ `using namespace`, a `A::B` value here will render a class `A::B::C::MyClass` in the diagram as `C::MyClass`, at most 1 value is supported * `generate_packages` - whether or not the class diagram should contain packages generated from namespaces or subdirectories * `package_type` - determines how the packages are inferred: `namespace` - use C++ namespaces, `directory` - use project's directory structure diff --git a/src/class_diagram/generators/mermaid/class_diagram_generator.cc b/src/class_diagram/generators/mermaid/class_diagram_generator.cc index eea063ef..8b9b21f2 100644 --- a/src/class_diagram/generators/mermaid/class_diagram_generator.cc +++ b/src/class_diagram/generators/mermaid/class_diagram_generator.cc @@ -293,8 +293,8 @@ void generator::generate(const concept_ &c, std::ostream &ostr) const ostr << " {" << '\n'; ostr << indent(2) << "<>\n"; - // TODO: add option to enable/disable this - if (c.requires_parameters().size() + c.requires_statements().size() > 0) { + if (config().generate_concept_requirements() && + (c.requires_parameters().size() + c.requires_statements().size() > 0)) { std::vector parameters; parameters.reserve(c.requires_parameters().size()); for (const auto &p : c.requires_parameters()) { diff --git a/src/class_diagram/generators/plantuml/class_diagram_generator.cc b/src/class_diagram/generators/plantuml/class_diagram_generator.cc index 451eb552..93d29bc1 100644 --- a/src/class_diagram/generators/plantuml/class_diagram_generator.cc +++ b/src/class_diagram/generators/plantuml/class_diagram_generator.cc @@ -383,8 +383,8 @@ void generator::generate(const concept_ &c, std::ostream &ostr) const ostr << " {" << '\n'; - // TODO: add option to enable/disable this - if (c.requires_parameters().size() + c.requires_statements().size() > 0) { + if (config().generate_concept_requirements() && + (c.requires_parameters().size() + c.requires_statements().size() > 0)) { std::vector parameters; parameters.reserve(c.requires_parameters().size()); for (const auto &p : c.requires_parameters()) { diff --git a/src/config/config.cc b/src/config/config.cc index 9a4f7bb5..a12e10af 100644 --- a/src/config/config.cc +++ b/src/config/config.cc @@ -197,6 +197,8 @@ void inheritable_diagram_options::inherit( puml.override(parent.puml); mermaid.override(parent.mermaid); generate_method_arguments.override(parent.generate_method_arguments); + generate_concept_requirements.override( + parent.generate_concept_requirements); generate_packages.override(parent.generate_packages); generate_template_argument_dependencies.override( parent.generate_template_argument_dependencies); diff --git a/src/config/config.h b/src/config/config.h index 029e934a..22c5ad65 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -530,6 +530,8 @@ struct inheritable_diagram_options { option mermaid{"mermaid", option_inherit_mode::kAppend}; option generate_method_arguments{ "generate_method_arguments", method_arguments::full}; + option generate_concept_requirements{ + "generate_concept_requirements", true}; option group_methods{"group_methods", true}; option member_order{ "member_order", member_order_t::lexical}; diff --git a/src/config/schema.h b/src/config/schema.h index 089c71c0..1569a9ea 100644 --- a/src/config/schema.h +++ b/src/config/schema.h @@ -174,6 +174,7 @@ types: # generate_method_arguments: !optional generate_method_arguments_t generate_packages: !optional bool + generate_concept_requirements: !optional bool package_type: !optional package_type_t generate_template_argument_dependencies: !optional bool skip_redundant_dependencies: !optional bool @@ -335,6 +336,7 @@ root: include_relations_also_as_members: !optional bool generate_method_arguments: !optional generate_method_arguments_t combine_free_functions_into_file_participants: !optional bool + generate_concept_requirements: !optional bool generate_return_types: !optional bool generate_condition_statements: !optional bool generate_message_comments: !optional bool diff --git a/src/config/yaml_decoders.cc b/src/config/yaml_decoders.cc index 96573065..3faf4347 100644 --- a/src/config/yaml_decoders.cc +++ b/src/config/yaml_decoders.cc @@ -626,6 +626,7 @@ template <> struct convert { get_option(node, rhs.layout); get_option(node, rhs.include_relations_also_as_members); get_option(node, rhs.generate_method_arguments); + get_option(node, rhs.generate_concept_requirements); get_option(node, rhs.group_methods); get_option(node, rhs.member_order); get_option(node, rhs.generate_packages); @@ -823,6 +824,7 @@ template <> struct convert { get_option(node, rhs.puml); get_option(node, rhs.mermaid); get_option(node, rhs.generate_method_arguments); + get_option(node, rhs.generate_concept_requirements); get_option(node, rhs.generate_packages); get_option(node, rhs.package_type); get_option(node, rhs.generate_template_argument_dependencies); diff --git a/src/config/yaml_emitters.cc b/src/config/yaml_emitters.cc index 3d1a0234..a28ea4f3 100644 --- a/src/config/yaml_emitters.cc +++ b/src/config/yaml_emitters.cc @@ -326,6 +326,7 @@ YAML::Emitter &operator<<( cd != nullptr) { out << cd->title; out << c.generate_method_arguments; + out << c.generate_concept_requirements; out << c.generate_packages; out << c.include_relations_also_as_members; if (c.relationship_hints) { diff --git a/tests/t00074/.clang-uml b/tests/t00074/.clang-uml new file mode 100644 index 00000000..211babf5 --- /dev/null +++ b/tests/t00074/.clang-uml @@ -0,0 +1,10 @@ +diagrams: + t00074_class: + type: class + glob: + - t00074.cc + generate_concept_requirements: false + include: + namespaces: + - clanguml::t00074 + using_namespace: clanguml::t00074 \ No newline at end of file diff --git a/tests/t00074/t00074.cc b/tests/t00074/t00074.cc new file mode 100644 index 00000000..b70abed9 --- /dev/null +++ b/tests/t00074/t00074.cc @@ -0,0 +1,16 @@ +namespace clanguml { +namespace t00074 { +template +concept fruit_c = requires(T t) { + T{}; + t.get_name(); + }; + +template +concept apple_c = fruit_c && requires(T t) { t.get_sweetness(); }; + +template +concept orange_c = fruit_c && requires(T t) { t.get_bitterness(); }; + +} +} \ No newline at end of file diff --git a/tests/t00074/test_case.h b/tests/t00074/test_case.h new file mode 100644 index 00000000..3409091c --- /dev/null +++ b/tests/t00074/test_case.h @@ -0,0 +1,91 @@ +/** + * tests/t00074/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("t00074", "[test-case][class]") +{ + auto [config, db] = load_config("t00074"); + + auto diagram = config.diagrams["t00074_class"]; + + REQUIRE(diagram->name == "t00074_class"); + + auto model = generate_class_diagram(*db, diagram); + + REQUIRE(model->name() == "t00074_class"); + + { + auto src = generate_class_puml(diagram, *model); + AliasMatcher _A(src); + + REQUIRE_THAT(src, StartsWith("@startuml")); + REQUIRE_THAT(src, EndsWith("@enduml\n")); + + REQUIRE_THAT(src, IsConcept(_A("fruit_c"))); + REQUIRE_THAT(src, IsConcept(_A("apple_c"))); + REQUIRE_THAT(src, IsConcept(_A("orange_c"))); + + REQUIRE_THAT( + src, IsConstraint(_A("apple_c"), _A("fruit_c"), "T")); + REQUIRE_THAT( + src, IsConstraint(_A("orange_c"), _A("fruit_c"), "T")); + + REQUIRE_THAT( + src, !IsConceptRequirement(_A("apple_c"), "t.get_sweetness()")); + REQUIRE_THAT(src, + !IsConceptRequirement(_A("orange_c"), "t.get_bitterness()")); + + save_puml(config.output_directory(), diagram->name + ".puml", src); + } + + { + auto j = generate_class_json(diagram, *model); + + using namespace json; + + REQUIRE(IsConcept(j, "fruit_c")); + REQUIRE(IsConcept(j, "apple_c")); + REQUIRE(IsConcept(j, "orange_c")); + + save_json(config.output_directory(), diagram->name + ".json", j); + } + + { + auto src = generate_class_mermaid(diagram, *model); + + mermaid::AliasMatcher _A(src); + using mermaid::IsConcept; + using mermaid::IsConceptRequirement; + using mermaid::IsConstraint; + + REQUIRE_THAT(src, IsConcept(_A("fruit_c"))); + REQUIRE_THAT(src, IsConcept(_A("apple_c"))); + REQUIRE_THAT(src, IsConcept(_A("orange_c"))); + + REQUIRE_THAT( + src, IsConstraint(_A("apple_c"), _A("fruit_c"), "T")); + REQUIRE_THAT( + src, IsConstraint(_A("orange_c"), _A("fruit_c"), "T")); + + REQUIRE_THAT( + src, !IsConceptRequirement(_A("apple_c"), "t.get_sweetness()")); + REQUIRE_THAT( + src, !IsConceptRequirement(_A("apple_c"), "t.get_bitterness()")); + + 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 931c71ce..22bc5097 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -415,6 +415,9 @@ using namespace clanguml::test::matchers; #include "t00072/test_case.h" #endif #include "t00073/test_case.h" +#if defined(ENABLE_CXX_STD_20_TEST_CASES) +#include "t00074/test_case.h" +#endif /// /// Sequence diagram tests diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index f4471719..1b7f90b0 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -216,6 +216,9 @@ test_cases: - name: t00073 title: Class diagram for template overload pattern description: + - name: t00074 + title: Test case for rendering concepts without requirements + description: Sequence diagrams: - name: t20001 title: Basic sequence diagram test case