diff --git a/src/class_diagram/generators/plantuml/class_diagram_generator.cc b/src/class_diagram/generators/plantuml/class_diagram_generator.cc index c41385cc..6961a439 100644 --- a/src/class_diagram/generators/plantuml/class_diagram_generator.cc +++ b/src/class_diagram/generators/plantuml/class_diagram_generator.cc @@ -107,10 +107,10 @@ void generator::generate_alias(const concept_ &c, std::ostream &ostr) const print_debug(c, ostr); if (m_config.generate_packages()) - ostr << "annotation" + ostr << "class" << " \"" << c.name(); else - ostr << "annotation" + ostr << "class" << " \"" << render_name(c.full_name()); ostr << "\" as " << c.alias() << '\n'; @@ -300,7 +300,7 @@ void generator::generate(const concept_ &c, std::ostream &ostr) const { namespace plantuml_common = clanguml::common::generators::plantuml; - std::string class_type{"annotation"}; + std::string class_type{"class"}; ostr << class_type << " " << c.alias() << " <>"; diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 5bfc8bc0..14d4ff23 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -415,8 +415,6 @@ bool translation_unit_visitor::TraverseConceptDecl(clang::ConceptDecl *cpt) // process 'requires (...)' declaration for (const auto *decl : constraint->getBody()->decls()) { - // decl->dump(); - if (const auto *parm_var_decl = clang::dyn_cast(decl); parm_var_decl) { @@ -491,38 +489,92 @@ void translation_unit_visitor::find_relationships_in_constraint_expression( if (expr == nullptr) return; - if (const auto *concept_specialization = - clang::dyn_cast(expr); - concept_specialization) { - if (concept_specialization->getNamedConcept() && - diagram().should_include(concept_specialization->getNamedConcept() - ->getQualifiedNameAsString())) { - auto target_id = get_ast_local_id( - concept_specialization->getNamedConcept()->getID()) - .value(); + common::if_dyn_cast( + expr, [&](const auto *cs) { + process_concept_specialization_relationships(c, cs); + }); - for (const auto ta : - concept_specialization->getTemplateArguments()) { - if (ta.getKind() == clang::TemplateArgument::Template) - ta.getAsTemplateOrTemplatePattern().dump(); - } - - c.add_relationship({relationship_t::kDependency, target_id}); - } - } - else if (const auto *constraint = - clang::dyn_cast(expr); - constraint) { + common::if_dyn_cast(expr, [&](const auto *re) { // TODO - } - else if (const auto *binop = clang::dyn_cast(expr); - binop) { - find_relationships_in_constraint_expression(c, binop->getLHS()); - find_relationships_in_constraint_expression(c, binop->getRHS()); - } - else if (const auto *unop = clang::dyn_cast(expr); - unop) { - find_relationships_in_constraint_expression(c, unop->getSubExpr()); + }); + + common::if_dyn_cast(expr, [&](const auto *op) { + find_relationships_in_constraint_expression(c, op->getLHS()); + find_relationships_in_constraint_expression(c, op->getRHS()); + }); + + common::if_dyn_cast(expr, [&](const auto *op) { + find_relationships_in_constraint_expression(c, op->getSubExpr()); + }); +} + +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())) { + auto target_id = get_ast_local_id(cpt->getID()).value(); + + std::vector constrained_template_params; + + size_t argument_index{}; + for (const auto ta : concept_specialization->getTemplateArguments()) { + + 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); + } + } + } + else { + auto type_name = + common::to_string(ta.getAsType(), cpt->getASTContext()); + LOG_DBG( + "=== Unsupported concept type parameter: {}", type_name); + } + argument_index++; + } + if (!constrained_template_params.empty()) + c.add_relationship( + {relationship_t::kDependency, target_id, access_t::kNone, + fmt::format( + "{}", fmt::join(constrained_template_params, ","))}); } } @@ -817,9 +869,19 @@ bool translation_unit_visitor::process_template_parameters( util::apply_if_not_null( template_type_parameter->getTypeConstraint() ->getNamedConcept(), - [&ct](const clang::ConceptDecl *named_concept) mutable { + [this, &ct, &templated_element]( + const clang::ConceptDecl *named_concept) mutable { ct.set_concept_constraint( named_concept->getQualifiedNameAsString()); + if (templated_element && + diagram().should_include( + named_concept->getQualifiedNameAsString())) { + templated_element.value().add_relationship( + {relationship_t::kDependency, + get_ast_local_id(named_concept->getID()) + .value(), + access_t::kNone, ct.name()}); + } }); } @@ -867,7 +929,7 @@ void translation_unit_visitor::process_template_record_containment( { assert(record.getParent()->isRecord()); - const auto *parent = record.getParent(); //->getOuterLexicalRecordContext(); + const auto *parent = record.getParent(); if (parent != nullptr) { if (const auto *record_decl = diff --git a/src/class_diagram/visitor/translation_unit_visitor.h b/src/class_diagram/visitor/translation_unit_visitor.h index 2c8d65d7..7cd87153 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.h +++ b/src/class_diagram/visitor/translation_unit_visitor.h @@ -293,5 +293,7 @@ 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/clang_utils.h b/src/common/clang_utils.h index 6b64ef0a..584e7af3 100644 --- a/src/common/clang_utils.h +++ b/src/common/clang_utils.h @@ -147,4 +147,16 @@ std::vector parse_unexposed_template_params( const std::string ¶ms, const std::function &ns_resolve, int depth = 0); + +template +void if_dyn_cast(P pointer, F &&func) +{ + if (pointer == nullptr) + return; + + if (const auto *dyn_cast_value = clang::dyn_cast(pointer); + dyn_cast_value) { + std::forward(func)(dyn_cast_value); + } +} } // namespace clanguml::common diff --git a/src/common/model/enums.h b/src/common/model/enums.h index e8c0779b..dd0ccea6 100644 --- a/src/common/model/enums.h +++ b/src/common/model/enums.h @@ -23,7 +23,7 @@ namespace clanguml::common::model { enum class diagram_t { kClass, kSequence, kPackage, kInclude }; -enum class access_t { kPublic, kProtected, kPrivate }; +enum class access_t { kPublic, kProtected, kPrivate, kNone }; enum class relationship_t { kNone, diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 26f64965..224fc65b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -18,7 +18,7 @@ endif(MSVC) list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_20 SUPPORTS_CXX_STD_20) if(SUPPORTS_CXX_STD_20 EQUAL -1 - OR ${LLVM_PACKAGE_VERSION} VERSION_LESS "15.0") + OR ${LLVM_PACKAGE_VERSION} VERSION_LESS "14.0") set(ENABLE_CXX_STD_20_TEST_CASES 0) foreach(CXX20_TC ${TEST_CASES_REQUIRING_CXX20}) list(FILTER TEST_CASE_SOURCES diff --git a/tests/t00056/t00056.cc b/tests/t00056/t00056.cc index 088b114b..57af9f4e 100644 --- a/tests/t00056/t00056.cc +++ b/tests/t00056/t00056.cc @@ -4,10 +4,10 @@ namespace clanguml { namespace t00056 { template -concept greater_than = sizeof(T) > sizeof(L); +concept greater_than_simple = sizeof(T) > sizeof(L); template -concept greater_than_requires = requires(T l, P r) +concept greater_than_with_requires = requires(T l, P r) { sizeof(l) > sizeof(r); }; @@ -60,8 +60,8 @@ struct B { T b; }; -// Anonymous concept requirement -template +// Anonymous concept requirement (TODO) +template requires requires(T t) { --t; @@ -71,5 +71,25 @@ struct C { T c; }; +template +requires max_four_bytes && max_four_bytes +struct D { +}; + +template +requires greater_than_with_requires +struct E { + T1 e1; + T2 e2; + T3 e3; +}; + +template +requires greater_than_simple +struct F { + T1 f1; + T2 f2; + T3 f3; +}; } } \ No newline at end of file