From 3509853dba68194faf1faa683b1e06bae9ba7331 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Sun, 26 Feb 2023 13:51:25 +0100 Subject: [PATCH] Implemented tests for concept test case 00056 --- .../visitor/translation_unit_visitor.cc | 89 ++++++++++--------- .../visitor/translation_unit_visitor.h | 13 ++- src/common/generators/plantuml/generator.cc | 2 + src/common/model/enums.cc | 2 + src/common/model/enums.h | 3 +- tests/t00056/test_case.h | 71 ++++++++++----- tests/test_cases.h | 19 ++++ 7 files changed, 132 insertions(+), 67 deletions(-) diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 14d4ff23..39906c2a 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -512,8 +512,14 @@ void translation_unit_visitor::process_concept_specialization_relationships( common::model::element &c, const clang::ConceptSpecializationExpr *concept_specialization) { - const auto *cpt = concept_specialization->getNamedConcept(); - if (cpt && diagram().should_include(cpt->getQualifiedNameAsString())) { + if (const auto *cpt = concept_specialization->getNamedConcept(); + should_include(cpt)) { + + const auto cpt_name = cpt->getNameAsString(); + + if (!get_ast_local_id(cpt->getID())) + return; + auto target_id = get_ast_local_id(cpt->getID()).value(); std::vector constrained_template_params; @@ -524,43 +530,9 @@ void translation_unit_visitor::process_concept_specialization_relationships( if (ta.getKind() == clang::TemplateArgument::Type) { auto type_name = common::to_string(ta.getAsType(), cpt->getASTContext()); - - if (const auto *nested_template_type = - ta.getAsType()->getAs(); - nested_template_type != nullptr) { - - auto declaration_text = common::get_source_text_raw( - concept_specialization->getSourceRange(), - source_manager()); - - if (!declaration_text.empty()) { - // Handle typename constraint in requires clause - if (type_name.find("type-parameter-") == 0) { - declaration_text = declaration_text.substr( - declaration_text.find(cpt->getNameAsString()) + - cpt->getNameAsString().size() + 1); - - auto template_params = - common::parse_unexposed_template_params( - declaration_text, - [](const auto &t) { return t; }); - - if (template_params.size() > argument_index) - type_name = - template_params[argument_index].to_string( - config().using_namespace(), false); - else { - LOG_DBG("Failed to find type specialization " - "for argument " - "{} at index {} in declaration " - "\n===\n{}\n===\n", - type_name, argument_index, - declaration_text); - } - } - constrained_template_params.push_back(type_name); - } - } + extract_constrained_template_param_name(concept_specialization, + cpt, constrained_template_params, argument_index, + type_name); } else { auto type_name = @@ -572,7 +544,7 @@ void translation_unit_visitor::process_concept_specialization_relationships( } if (!constrained_template_params.empty()) c.add_relationship( - {relationship_t::kDependency, target_id, access_t::kNone, + {relationship_t::kConstraint, target_id, access_t::kNone, fmt::format( "{}", fmt::join(constrained_template_params, ","))}); } @@ -877,7 +849,7 @@ bool translation_unit_visitor::process_template_parameters( diagram().should_include( named_concept->getQualifiedNameAsString())) { templated_element.value().add_relationship( - {relationship_t::kDependency, + {relationship_t::kConstraint, get_ast_local_id(named_concept->getID()) .value(), access_t::kNone, ct.name()}); @@ -1301,7 +1273,6 @@ void translation_unit_visitor:: deduced_auto_decl->getQualifiedNameAsString(); if (diagram().should_include(template_field_decl_name)) { - relationship r{relationship_t::kDependency, template_specialization_model.get().id()}; @@ -2715,4 +2686,38 @@ translation_unit_visitor::get_ast_local_id(int64_t local_id) const return local_ast_id_map_.at(local_id); } + +void translation_unit_visitor::extract_constrained_template_param_name( + const clang::ConceptSpecializationExpr *concept_specialization, + const clang::ConceptDecl *cpt, + std::vector &constrained_template_params, + size_t argument_index, std::string &type_name) const +{ + const auto full_declaration_text = common::get_source_text_raw( + concept_specialization->getSourceRange(), source_manager()); + + if (!full_declaration_text.empty()) { + // Handle typename constraint in requires clause + if (type_name.find("type-parameter-") == 0) { + const auto concept_declaration_text = full_declaration_text.substr( + full_declaration_text.find(cpt->getNameAsString()) + + cpt->getNameAsString().size() + 1); + + auto template_params = common::parse_unexposed_template_params( + concept_declaration_text, [](const auto &t) { return t; }); + + if (template_params.size() > argument_index) + type_name = template_params[argument_index].to_string( + config().using_namespace(), false); + } + constrained_template_params.push_back(type_name); + } +} + +bool translation_unit_visitor::should_include(const clang::NamedDecl *decl) +{ + return decl != nullptr && + diagram().should_include(decl->getQualifiedNameAsString()); +} + } // namespace clanguml::class_diagram::visitor diff --git a/src/class_diagram/visitor/translation_unit_visitor.h b/src/class_diagram/visitor/translation_unit_visitor.h index 7cd87153..04192cd3 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.h +++ b/src/class_diagram/visitor/translation_unit_visitor.h @@ -111,6 +111,8 @@ public: void finalize(); private: + bool should_include(const clang::NamedDecl *decl); + std::unique_ptr create_class_declaration(clang::CXXRecordDecl *cls); @@ -268,6 +270,15 @@ private: bool simplify_system_template(common::model::template_parameter &ct, const std::string &full_name) const; + void process_concept_specialization_relationships(common::model::element &c, + const clang::ConceptSpecializationExpr *concept_specialization); + + void extract_constrained_template_param_name( + const clang::ConceptSpecializationExpr *concept_specialization, + const clang::ConceptDecl *cpt, + std::vector &constrained_template_params, + size_t argument_index, std::string &type_name) const; + /// Store the mapping from local clang entity id (obtained using /// getID()) method to clang-uml global id void set_ast_local_id( @@ -293,7 +304,5 @@ private: std::tuple> anonymous_struct_relationships_; - void process_concept_specialization_relationships(common::model::element &c, - const clang::ConceptSpecializationExpr *concept_specialization); }; } // namespace clanguml::class_diagram::visitor diff --git a/src/common/generators/plantuml/generator.cc b/src/common/generators/plantuml/generator.cc index e83e8de9..c384ddd2 100644 --- a/src/common/generators/plantuml/generator.cc +++ b/src/common/generators/plantuml/generator.cc @@ -37,6 +37,8 @@ std::string to_plantuml(relationship_t r, const std::string &style) return style.empty() ? "<.." : fmt::format("<.[{}].", style); case relationship_t::kDependency: return style.empty() ? "..>" : fmt::format(".[{}].>", style); + case relationship_t::kConstraint: + return style.empty() ? "..>" : fmt::format(".[{}].>", style); case relationship_t::kAlias: return style.empty() ? ".." : fmt::format(".[{}].", style); default: diff --git a/src/common/model/enums.cc b/src/common/model/enums.cc index a0b66ffc..feeb740c 100644 --- a/src/common/model/enums.cc +++ b/src/common/model/enums.cc @@ -46,6 +46,8 @@ std::string to_string(relationship_t r) return "dependency"; case relationship_t::kAlias: return "alias"; + case relationship_t::kConstraint: + return "constraint"; default: assert(false); return ""; diff --git a/src/common/model/enums.h b/src/common/model/enums.h index dd0ccea6..399c812f 100644 --- a/src/common/model/enums.h +++ b/src/common/model/enums.h @@ -36,7 +36,8 @@ enum class relationship_t { kInstantiation, kFriendship, kAlias, - kDependency + kDependency, + kConstraint }; /// Types of sequence diagram activity elements diff --git a/tests/t00056/test_case.h b/tests/t00056/test_case.h index 48260920..caa3ec9e 100644 --- a/tests/t00056/test_case.h +++ b/tests/t00056/test_case.h @@ -35,32 +35,59 @@ TEST_CASE("t00056", "[test-case][class]") REQUIRE_THAT(puml, EndsWith("@enduml\n")); // Check if all classes exist - // REQUIRE_THAT(puml, IsClass(_A("A"))); + REQUIRE_THAT(puml, IsConcept(_A("greater_than_simple"))); + REQUIRE_THAT(puml, IsConcept(_A("greater_than_with_requires"))); + REQUIRE_THAT(puml, IsConcept(_A("max_four_bytes"))); + REQUIRE_THAT(puml, IsConcept(_A("iterable"))); + REQUIRE_THAT(puml, IsConcept(_A("has_value_type"))); + REQUIRE_THAT(puml, IsConcept(_A("convertible_to_string"))); + REQUIRE_THAT(puml, IsConcept(_A("iterable_with_value_type"))); + REQUIRE_THAT(puml, IsConcept(_A("iterable_or_small_value_type"))); // Check if class templates exist - // REQUIRE_THAT(puml, IsClassTemplate("A", "T,P,CMP,int N")); - - // Check if all enums exist - // REQUIRE_THAT(puml, IsEnum(_A("Lights"))); - - // Check if all inner classes exist - // REQUIRE_THAT(puml, IsInnerClass(_A("A"), _A("AA"))); - - // Check if all inheritance relationships exist - // REQUIRE_THAT(puml, IsBaseClass(_A("Base"), _A("Child"))); - - // Check if all methods exist - // REQUIRE_THAT(puml, (IsMethod("foo"))); - - // Check if all fields exist - // REQUIRE_THAT(puml, (IsField("private_member", "int"))); + REQUIRE_THAT(puml, IsClassTemplate("A", "max_four_bytes T")); + REQUIRE_THAT(puml, IsClassTemplate("B", "T")); + REQUIRE_THAT(puml, IsClassTemplate("C", "convertible_to_string T")); + REQUIRE_THAT( + puml, IsClassTemplate("D", "iterable T1,T2,iterable T3,T4,T5")); + REQUIRE_THAT(puml, IsClassTemplate("E", "T1,T2,T3")); + REQUIRE_THAT(puml, IsClassTemplate("F", "T1,T2,T3")); // Check if all relationships exist - // REQUIRE_THAT(puml, IsAssociation(_A("D"), _A("A"), "-as")); - // REQUIRE_THAT(puml, IsDependency(_A("R"), _A("B"))); - // REQUIRE_THAT(puml, IsAggregation(_A("R"), _A("D"))); - // REQUIRE_THAT(puml, IsComposition(_A("R"), _A("D"))); - // REQUIRE_THAT(puml, IsInstantiation(_A("ABCD::F"), _A("F"))); + REQUIRE_THAT(puml, + IsConstraint(_A("A"), _A("max_four_bytes"), "T")); + + REQUIRE_THAT(puml, + IsConstraint(_A("D"), + _A("max_four_bytes"), "T2")); + REQUIRE_THAT(puml, + IsConstraint(_A("D"), + _A("max_four_bytes"), "T5")); + REQUIRE_THAT(puml, + IsConstraint(_A("D"), + _A("iterable"), "T1")); + REQUIRE_THAT(puml, + IsConstraint(_A("D"), + _A("iterable"), "T3")); + + REQUIRE_THAT(puml, + IsConstraint( + _A("iterable_with_value_type"), _A("has_value_type"), "T")); + + REQUIRE_THAT(puml, + IsConstraint(_A("iterable_or_small_value_type"), + _A("max_four_bytes"), "T")); + REQUIRE_THAT(puml, + IsConstraint(_A("iterable_or_small_value_type"), + _A("iterable_with_value_type"), "T")); + + REQUIRE_THAT(puml, + IsConstraint( + _A("E"), _A("greater_than_with_requires"), "T1,T3")); + + REQUIRE_THAT(puml, + IsConstraint( + _A("F"), _A("greater_than_simple"), "T1,T3")); save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml); } \ No newline at end of file diff --git a/tests/test_cases.h b/tests/test_cases.h index d3e4351f..25134416 100644 --- a/tests/test_cases.h +++ b/tests/test_cases.h @@ -281,6 +281,13 @@ ContainsMatcher IsClassTemplate(std::string const &str, fmt::format("class \"{}<{}>\"", str, tmplt), caseSensitivity)); } +ContainsMatcher IsConcept(std::string const &str, + CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) +{ + return ContainsMatcher( + CasedString("class " + str + " <>", caseSensitivity)); +} + ContainsMatcher IsEnum(std::string const &str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) { @@ -426,6 +433,18 @@ ContainsMatcher IsDependency(std::string const &from, std::string const &to, CasedString(fmt::format("{} ..> {}", from, to), caseSensitivity)); } +ContainsMatcher IsConstraint(std::string const &from, std::string const &to, + std::string const &label = {}, + CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) +{ + if (label.empty()) + return ContainsMatcher( + CasedString(fmt::format("{} ..> {}", from, to), caseSensitivity)); + else + return ContainsMatcher(CasedString( + fmt::format("{} ..> {} : {}", from, to, label), caseSensitivity)); +} + ContainsMatcher IsLayoutHint(std::string const &from, std::string const &hint, std::string const &to, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)