diff --git a/src/class_diagram/model/class.cc b/src/class_diagram/model/class.cc index 09eb7a66..45fb018f 100644 --- a/src/class_diagram/model/class.cc +++ b/src/class_diagram/model/class.cc @@ -126,9 +126,19 @@ int class_::calculate_template_specialization_match( { int res{}; + LOG_DBG("### Comparing {} with {}", this->full_name(false), + other.full_name(false)); + + if (this->full_name(false) == + "clanguml::t00044::signal_handler" && + other.full_name(false) == + "clanguml::t00044::signal_handler") { + LOG_DBG("AAAAAAAAA"); + } + const std::string left = name_and_ns(); // TODO: handle variadic templates - if ((name_and_ns() != full_name) || + if ((left != full_name) || (templates().size() != other.templates().size())) { return res; } @@ -142,6 +152,10 @@ int class_::calculate_template_specialization_match( res++; } else if (other_template_arg.is_specialization_of(template_arg)) { + if (template_arg.is_function_template() && + other_template_arg.is_function_template()) { + res++; + } continue; } else { diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 7a583572..c3e2fb03 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -250,7 +250,7 @@ bool translation_unit_visitor::VisitTypeAliasTemplateDecl( return true; auto template_specialization_ptr = - build_template_instantiation(*template_type_specialization_ptr); + build_template_instantiation(cls, *template_type_specialization_ptr); if (!template_specialization_ptr) return true; @@ -630,8 +630,8 @@ void translation_unit_visitor::process_concept_specialization_relationships( std::vector constrained_template_params; size_t argument_index{}; - for (const auto ta : concept_specialization->getTemplateArguments()) { + for (const auto ta : concept_specialization->getTemplateArguments()) { if (ta.getKind() == clang::TemplateArgument::Type) { auto type_name = common::to_string(ta.getAsType(), cpt->getASTContext()); @@ -1092,7 +1092,7 @@ void translation_unit_visitor::process_class_bases( base.getType()->getAs(); tsp != nullptr) { auto template_specialization_ptr = - build_template_instantiation(*tsp, {}); + build_template_instantiation(cls, *tsp, {}); if (template_specialization_ptr) { cp.set_id(template_specialization_ptr->id()); } @@ -1350,7 +1350,7 @@ void translation_unit_visitor::process_method( } else if (const auto *atsp = underlying_type->getAs(); atsp != nullptr) { - process_function_parameter_find_relatinoships_in_autotype(c, atsp); + process_function_parameter_find_relationships_in_autotype(c, atsp); } LOG_DBG("Adding method: {}", method.name()); @@ -1359,7 +1359,7 @@ void translation_unit_visitor::process_method( } void translation_unit_visitor:: - process_function_parameter_find_relatinoships_in_autotype( + process_function_parameter_find_relationships_in_autotype( class_ &c, const clang::AutoType *atsp) { auto desugared_atsp = atsp->getDeducedType(); @@ -1665,8 +1665,9 @@ void translation_unit_visitor:: template_instantiation_type.getTemplateName().getAsTemplateDecl())) return; - auto template_specialization_ptr = - build_template_instantiation(template_instantiation_type, &c); + auto template_specialization_ptr = build_template_instantiation( + template_instantiation_type.getTemplateName().getAsTemplateDecl(), + template_instantiation_type, &c); if (template_instantiation_type.isDependentType()) { if (template_specialization_ptr) { @@ -1816,9 +1817,93 @@ void translation_unit_visitor::process_template_specialization_argument( // If this is a nested template type - add nested templates as // template arguments - if (const auto *nested_template_type = - arg.getAsType()->getAs(); - nested_template_type != nullptr) { + if (const auto *function_type = + arg.getAsType()->getAs(); + function_type != nullptr) { + + auto a = template_parameter::make_template_type({}); + + a.set_function_template(true); + + // Set function template return type + const auto return_type_name = + function_type->getReturnType().getAsString(); + + // Try to match the return type to template parameter in case + // the type name is in the form 'type-parameter-X-Y' + auto maybe_return_arg = + get_template_argument_from_type_parameter_string( + cls, return_type_name); + + if (maybe_return_arg) + a .add_template_param(*maybe_return_arg); + else { + a.add_template_param( + template_parameter::make_argument(return_type_name)); + } + + // Set function template argument types + for (const auto ¶m_type : function_type->param_types()) { + auto maybe_arg = get_template_argument_from_type_parameter_string( + cls, param_type.getAsString()); + + if (maybe_arg) { + a.add_template_param(*maybe_arg); + continue; + } + + if (param_type->isBuiltinType()) { + a.add_template_param(template_parameter::make_argument( + param_type.getAsString())); + continue; + } + + const auto *param_record_type = + param_type->getAs(); + if (param_record_type == nullptr) + continue; + +// auto *classTemplateSpecialization = +// llvm::dyn_cast( +// param_type->getAsRecordDecl()); +/* + if (classTemplateSpecialization != nullptr) { + // Read arg info as needed. + auto nested_template_instantiation = + build_template_instantiation_from_class_template_specialization( + *classTemplateSpecialization, *param_record_type, + diagram().should_include( + full_template_specialization_name) + ? std::make_optional(&template_instantiation) + : parent); + + const auto nested_template_name = + classTemplateSpecialization->getQualifiedNameAsString(); + + if (nested_template_instantiation) { + if (parent.has_value()) + parent.value()->add_relationship( + {relationship_t::kDependency, + nested_template_instantiation->id()}); + } + + auto nested_template_instantiation_full_name = + nested_template_instantiation->full_name(false); + if (diagram().should_include( + nested_template_instantiation_full_name)) { + diagram().add_class( + std::move(nested_template_instantiation)); + } + }*/ + + } + argument = a; + + } + else if (const auto *nested_template_type = + arg.getAsType() + ->getAs(); + nested_template_type != nullptr) { argument = template_parameter::make_argument({}); const auto nested_template_name = @@ -1829,7 +1914,7 @@ void translation_unit_visitor::process_template_specialization_argument( argument->set_type(nested_template_name); auto nested_template_instantiation = build_template_instantiation( - *nested_template_type, {&template_instantiation}); + cls, *nested_template_type, {&template_instantiation}); argument->set_id(nested_template_instantiation->id()); @@ -2054,7 +2139,7 @@ std::unique_ptr translation_unit_visitor:: std::hash{}(full_template_specialization_name) >> 4U)); build_template_instantiation_process_template_arguments(parent, - template_base_params, + &template_specialization, template_base_params, template_specialization.getTemplateArgs().asArray(), template_instantiation, full_template_specialization_name, template_decl); @@ -2106,6 +2191,7 @@ std::unique_ptr translation_unit_visitor:: } std::unique_ptr translation_unit_visitor::build_template_instantiation( + const clang::Decl *cls, const clang::TemplateSpecializationType &template_type_decl, std::optional parent) { @@ -2136,7 +2222,9 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( // auto template_instantiation_ptr = std::make_unique(config_.using_namespace()); + auto &template_instantiation = *template_instantiation_ptr; + std::string full_template_specialization_name = common::to_string( template_type.desugar(), template_type.getTemplateName().getAsTemplateDecl()->getASTContext()); @@ -2240,7 +2328,7 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( base_index++; } - build_template_instantiation_process_template_arguments(parent, + build_template_instantiation_process_template_arguments(parent, cls, template_base_params, template_type.template_arguments(), template_instantiation, full_template_specialization_name, template_decl); @@ -2302,6 +2390,7 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( void translation_unit_visitor:: build_template_instantiation_process_template_arguments( std::optional &parent, + const clang::Decl *cls, std::deque> &template_base_params, const clang::ArrayRef &template_args, class_ &template_instantiation, @@ -2318,8 +2407,8 @@ void translation_unit_visitor:: } else if (argument_kind == clang::TemplateArgument::Type) { argument = build_template_instantiation_process_type_argument( - parent, full_template_specialization_name, template_decl, arg, - template_instantiation); + parent, cls, full_template_specialization_name, template_decl, + arg, template_instantiation); } else if (argument_kind == clang::TemplateArgument::Integral) { argument = @@ -2374,6 +2463,7 @@ template_parameter translation_unit_visitor:: template_parameter translation_unit_visitor::build_template_instantiation_process_type_argument( std::optional &parent, + const clang::Decl *cls, const std::string &full_template_specialization_name, const clang::TemplateDecl *template_decl, const clang::TemplateArgument &arg, class_ &template_instantiation) @@ -2382,13 +2472,45 @@ translation_unit_visitor::build_template_instantiation_process_type_argument( auto argument = template_parameter::make_argument({}); - // If this is a nested template type - add nested templates as - // template arguments if (const auto *function_type = arg.getAsType()->getAs(); function_type != nullptr) { + argument.set_function_template(true); + + // Set function template return type + const auto return_type_name = + function_type->getReturnType().getAsString(); + + // Try to match the return type to template parameter in case + // the type name is in the form 'type-parameter-X-Y' + auto maybe_return_arg = + get_template_argument_from_type_parameter_string( + cls, return_type_name); + + if (maybe_return_arg) + argument.add_template_param(*maybe_return_arg); + else { + argument.add_template_param( + template_parameter::make_argument(return_type_name)); + } + + // Set function template argument types for (const auto ¶m_type : function_type->param_types()) { + auto maybe_arg = get_template_argument_from_type_parameter_string( + cls, param_type.getAsString()); + + if (maybe_arg) { + argument.add_template_param(*maybe_arg); + continue; + } + + if (param_type->isBuiltinType()) { + argument.add_template_param(template_parameter::make_argument( + param_type.getAsString())); + continue; + } + const auto *param_record_type = param_type->getAs(); if (param_record_type == nullptr) @@ -2428,6 +2550,14 @@ translation_unit_visitor::build_template_instantiation_process_type_argument( } } } + else if (const auto maybe_arg = + get_template_argument_from_type_parameter_string( + cls, arg.getAsType().getAsString()); + maybe_arg) { + // The type is only in the form 'type-parameter-X-Y' so we have + // to match it to a template parameter name in the 'cls' template + argument = *maybe_arg; + } else if (const auto *nested_template_type = arg.getAsType()->getAs(); nested_template_type != nullptr) { @@ -2439,7 +2569,7 @@ translation_unit_visitor::build_template_instantiation_process_type_argument( argument.set_type(nested_type_name); auto nested_template_instantiation = - build_template_instantiation(*nested_template_type, + build_template_instantiation(cls, *nested_template_type, diagram().should_include(full_template_specialization_name) ? std::make_optional(&template_instantiation) : parent); @@ -2492,6 +2622,40 @@ translation_unit_visitor::build_template_instantiation_process_type_argument( return argument; } +std::optional +translation_unit_visitor::get_template_argument_from_type_parameter_string( + const clang::Decl *decl, const std::string &return_type_name) const +{ + if (const auto *template_decl = + llvm::dyn_cast(decl); + template_decl && return_type_name.find("type-parameter-") == 0) { + + [[maybe_unused]] const auto [depth, index] = + common::extract_template_parameter_index(return_type_name); + + std::string param_name = return_type_name; + + for (auto i = 0U; + i < template_decl->getDescribedTemplateParams()->size(); i++) { + const auto *param = + template_decl->getDescribedTemplateParams()->getParam(i); + + if (i == index) { + param_name = param->getNameAsString(); + + auto template_param = + template_parameter::make_template_type(param_name); + + template_param.is_variadic(param->isParameterPack()); + + return template_param; + } + } + } + + return {}; +} + template_parameter translation_unit_visitor:: build_template_instantiation_process_integral_argument( const clang::TemplateArgument &arg) const @@ -2690,8 +2854,8 @@ void translation_unit_visitor::process_field( !field_type_is_template_template_parameter) { // Build the template instantiation for the field type - auto template_specialization_ptr = - build_template_instantiation(*template_field_type, {&c}); + auto template_specialization_ptr = build_template_instantiation( + &field_declaration, *template_field_type, {&c}); if (!field.skip_relationship() && template_specialization_ptr) { const auto &template_specialization = *template_specialization_ptr; diff --git a/src/class_diagram/visitor/translation_unit_visitor.h b/src/class_diagram/visitor/translation_unit_visitor.h index e18573a8..b64bad17 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.h +++ b/src/class_diagram/visitor/translation_unit_visitor.h @@ -187,7 +187,7 @@ private: bool break_on_first_aggregation = false); std::unique_ptr - build_template_instantiation( + build_template_instantiation(const clang::Decl *cls, const clang::TemplateSpecializationType &template_type, std::optional parent = {}); @@ -205,6 +205,7 @@ private: void build_template_instantiation_process_template_arguments( std::optional &parent, + const clang::Decl *cls, std::deque> &template_base_params, const clang::ArrayRef &template_args, model::class_ &template_instantiation, @@ -226,6 +227,7 @@ private: template_parameter build_template_instantiation_process_type_argument( std::optional &parent, + const clang::Decl *cls, const std::string &full_template_specialization_name, const clang::TemplateDecl *template_decl, const clang::TemplateArgument &arg, @@ -240,7 +242,7 @@ private: void process_record_parent( clang::RecordDecl *cls, class_ &c, const namespace_ &ns); - void process_function_parameter_find_relatinoships_in_autotype( + void process_function_parameter_find_relationships_in_autotype( model::class_ &c, const clang::AutoType *atsp); void process_function_parameter_find_relationships_in_template( @@ -279,6 +281,10 @@ private: std::vector &constrained_template_params, size_t argument_index, std::string &type_name) const; + std::optional + get_template_argument_from_type_parameter_string( + const clang::Decl *decl, const std::string &return_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( diff --git a/src/common/clang_utils.cc b/src/common/clang_utils.cc index 07eb980b..6d203502 100644 --- a/src/common/clang_utils.cc +++ b/src/common/clang_utils.cc @@ -250,6 +250,19 @@ std::string get_source_text( return get_source_text_raw(printable_range, sm); } +std::pair extract_template_parameter_index( + const std::string &type_parameter) +{ + assert(type_parameter.find("type-parameter-") == 0); + + auto toks = + util::split(type_parameter.substr(strlen("type-parameter-")), "-"); + + assert(toks.size() == 2); + + return {std::stoi(toks.at(0)), std::stoi(toks.at(1))}; +} + bool is_subexpr_of(const clang::Stmt *parent_stmt, const clang::Stmt *sub_stmt) { if (parent_stmt == nullptr || sub_stmt == nullptr) diff --git a/src/common/clang_utils.h b/src/common/clang_utils.h index 0ed46aa5..18b6c548 100644 --- a/src/common/clang_utils.h +++ b/src/common/clang_utils.h @@ -95,6 +95,9 @@ std::string get_source_text_raw( std::string get_source_text( clang::SourceRange range, const clang::SourceManager &sm); +std::pair extract_template_parameter_index( + const std::string &type_parameter); + /** * @brief Check if an expression is contained in another expression * diff --git a/src/common/model/template_parameter.cc b/src/common/model/template_parameter.cc index d3a80903..213ed51b 100644 --- a/src/common/model/template_parameter.cc +++ b/src/common/model/template_parameter.cc @@ -111,6 +111,11 @@ bool template_parameter::is_variadic() const noexcept { return is_variadic_; } bool template_parameter::is_specialization_of( const template_parameter &ct) const { + if(is_function_template() && ct.is_function_template()) { + bool res{true}; + + } + return (ct.is_template_parameter() || ct.is_template_template_parameter()) && !is_template_parameter(); @@ -139,6 +144,9 @@ bool operator==(const template_parameter &l, const template_parameter &r) if (l.is_template_parameter() != r.is_template_parameter()) return res; + if (l.is_function_template() != r.is_function_template()) + return res; + if (l.is_template_parameter()) { // If this is a template parameter (e.g. 'typename T' or 'typename U' // we don't actually care what it is called @@ -165,6 +173,19 @@ std::string template_parameter::to_string( assert(!(type().has_value() && concept_constraint().has_value())); + if (is_function_template()) { + auto it = template_params_.begin(); + auto return_type = it->to_string(using_namespace, relative); + std::advance(it, 1); + + std::vector function_args; + for (; it != template_params_.end(); it++) + function_args.push_back(it->to_string(using_namespace, relative)); + + return fmt::format( + "{}({})", return_type, fmt::join(function_args, ",")); + } + std::string res; const auto maybe_type = type(); if (maybe_type) { diff --git a/src/common/model/template_parameter.h b/src/common/model/template_parameter.h index 516ca258..fe9a1c74 100644 --- a/src/common/model/template_parameter.h +++ b/src/common/model/template_parameter.h @@ -176,6 +176,10 @@ public: void set_unexposed(bool unexposed) { is_unexposed_ = unexposed; } + void set_function_template(bool ft) { is_function_template_ = ft; } + + bool is_function_template() const { return is_function_template_; } + private: template_parameter() = default; @@ -202,11 +206,14 @@ private: /// Whether the template parameter is variadic bool is_variadic_{false}; + bool is_function_template_{false}; + /// Stores optional fully qualified name of constraint for this template /// parameter std::optional concept_constraint_; // Nested template parameters + // If this is a function template, the first element is the return type std::vector template_params_; std::optional id_; diff --git a/tests/t00044/t00044.cc b/tests/t00044/t00044.cc index 33110d68..24f391a5 100644 --- a/tests/t00044/t00044.cc +++ b/tests/t00044/t00044.cc @@ -29,6 +29,8 @@ sink(signal_handler &) signal_handler int_handler; -sink sink1{int_handler}; +struct R { + sink> sink1{int_handler}; +}; } // namespace clanguml::t00044 diff --git a/tests/t00044/test_case.h b/tests/t00044/test_case.h index e9f40a1f..425502b0 100644 --- a/tests/t00044/test_case.h +++ b/tests/t00044/test_case.h @@ -35,10 +35,28 @@ TEST_CASE("t00044", "[test-case][class]") REQUIRE_THAT(puml, StartsWith("@startuml")); REQUIRE_THAT(puml, EndsWith("@enduml\n")); - // TODO: - // Check dependants filter - // REQUIRE_THAT(puml, IsClassTemplate("signal_handler", - // "Ret,Args...,A")); + REQUIRE_THAT(puml, IsClassTemplate("sink", "T")); + REQUIRE_THAT(puml, IsClassTemplate("signal_handler", "T,A")); + + REQUIRE_THAT(puml, IsClassTemplate("signal_handler", "Ret(Args...),A")); + REQUIRE_THAT(puml, IsClassTemplate("signal_handler", "void(int),bool")); + + REQUIRE_THAT(puml, + IsClassTemplate( + "sink", "clanguml::t00044::signal_handler")); + + REQUIRE_THAT(puml, + IsInstantiation(_A("sink"), + _A("sink>"))); + + REQUIRE_THAT(puml, IsClassTemplate("signal_handler", "T,A")); + REQUIRE_THAT(puml, + IsInstantiation(_A("signal_handler"), + _A("signal_handler"))); + + REQUIRE_THAT(puml, + IsInstantiation(_A("signal_handler"), + _A("signal_handler"))); save_puml( config.output_directory() + "/" + diagram->name + ".puml", puml);