From 6d4533018b6e35ee8bee375ed87c9b045e4556c3 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Thu, 16 Mar 2023 01:53:10 +0100 Subject: [PATCH] Refactoring template_parameter model --- .../json/class_diagram_generator.cc | 25 ++- .../visitor/translation_unit_visitor.cc | 178 +++++++++--------- .../visitor/translation_unit_visitor.h | 21 +-- src/common/clang_utils.cc | 18 +- src/common/model/template_parameter.cc | 99 +++++++--- src/common/model/template_parameter.h | 116 ++++++++++-- .../visitor/translation_unit_visitor.cc | 137 +++++++------- .../visitor/translation_unit_visitor.h | 23 ++- tests/t00008/test_case.h | 2 +- tests/test_util.cc | 28 +-- 10 files changed, 393 insertions(+), 254 deletions(-) diff --git a/src/class_diagram/generators/json/class_diagram_generator.cc b/src/class_diagram/generators/json/class_diagram_generator.cc index 581a5cfd..39a8ebaa 100644 --- a/src/class_diagram/generators/json/class_diagram_generator.cc +++ b/src/class_diagram/generators/json/class_diagram_generator.cc @@ -44,15 +44,22 @@ void to_json(nlohmann::json &j, const element &c) void to_json(nlohmann::json &j, const template_parameter &c) { - j["type"] = c.type(); - j["name"] = c.name(); - if (!c.default_value().empty()) - j["default_value"] = c.default_value(); - j["is_template_parameter"] = c.is_template_parameter(); - j["is_template_template_parameter"] = c.is_template_template_parameter(); - if (const auto &constraint = c.concept_constraint(); constraint) - j["concept_constraint"] = constraint.value(); - j["is_variadic"] = c.is_variadic(); + j["kind"] = to_string(c.kind()); + + if(c.kind() == template_parameter_kind_t::template_type) { + j["name"] = c.name().value(); + } + + +// j["type"] = c.type(); +// j["name"] = c.name(); +// if (!c.default_value().empty()) +// j["default_value"] = c.default_value(); +// j["is_template_parameter"] = c.is_template_parameter(); +// j["is_template_template_parameter"] = c.is_template_template_parameter(); +// if (const auto &constraint = c.concept_constraint(); constraint) +// j["concept_constraint"] = constraint.value(); +// j["is_variadic"] = c.is_variadic(); } void to_json(nlohmann::json &j, const relationship &c) diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index d80d1e68..c3f66a00 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -949,12 +949,15 @@ bool translation_unit_visitor::process_template_parameters( nullptr) { const auto *template_type_parameter = clang::dyn_cast_or_null(parameter); - template_parameter ct; - ct.set_type(""); - ct.is_template_parameter(true); - ct.set_name(template_type_parameter->getNameAsString()); - ct.set_default_value(""); - ct.is_variadic(template_type_parameter->isParameterPack()); + + std::optional default_arg; + if (template_type_parameter->hasDefaultArgument()) { + default_arg = + template_type_parameter->getDefaultArgument().getAsString(); + } + auto ct = template_parameter::make_template_type( + template_type_parameter->getNameAsString(), default_arg, + template_type_parameter->isParameterPack()); if (template_type_parameter->getTypeConstraint() != nullptr) { util::apply_if_not_null( @@ -970,7 +973,7 @@ bool translation_unit_visitor::process_template_parameters( {relationship_t::kConstraint, get_ast_local_id(named_concept->getID()) .value(), - access_t::kNone, ct.name()}); + access_t::kNone, ct.name().value()}); } }); } @@ -982,12 +985,14 @@ bool translation_unit_visitor::process_template_parameters( const auto *template_nontype_parameter = clang::dyn_cast_or_null( parameter); - template_parameter ct; - ct.set_type(template_nontype_parameter->getType().getAsString()); - ct.set_name(template_nontype_parameter->getNameAsString()); - ct.is_template_parameter(false); - ct.set_default_value(""); - ct.is_variadic(template_nontype_parameter->isParameterPack()); + std::optional default_arg; + if (template_nontype_parameter->hasDefaultArgument()) + default_arg = common::to_string( + template_nontype_parameter->getDefaultArgument()); + auto ct = template_parameter::make_non_type_template( + template_nontype_parameter->getType().getAsString(), + template_nontype_parameter->getNameAsString(), default_arg, + template_nontype_parameter->isParameterPack()); c.add_template(std::move(ct)); } @@ -996,12 +1001,15 @@ bool translation_unit_visitor::process_template_parameters( const auto *template_template_parameter = clang::dyn_cast_or_null( parameter); - template_parameter ct; - ct.set_type(""); - ct.set_name(template_template_parameter->getNameAsString() + "<>"); - ct.is_template_parameter(true); - ct.set_default_value(""); - ct.is_variadic(template_template_parameter->isParameterPack()); + std::optional default_arg; + if (template_template_parameter->hasDefaultArgument()) + default_arg = common::to_string( + template_template_parameter->getDefaultArgument() + .getArgument() + .getAsExpr()); + auto ct = template_parameter::make_template_template_type( + template_template_parameter->getNameAsString(), default_arg, + template_template_parameter->isParameterPack()); c.add_template(std::move(ct)); } @@ -1798,8 +1806,7 @@ void translation_unit_visitor::process_template_specialization_argument( const auto argument_kind = arg.getKind(); if (argument_kind == clang::TemplateArgument::Type) { - template_parameter argument; - argument.is_template_parameter(false); + auto argument = template_parameter::make_argument({}); // If this is a nested template type - add nested templates as // template arguments @@ -1812,7 +1819,7 @@ void translation_unit_visitor::process_template_specialization_argument( .getAsTemplateDecl() ->getQualifiedNameAsString(); - argument.set_name(nested_template_name); + argument.set_type(nested_template_name); auto nested_template_instantiation = build_template_instantiation( *nested_template_type, {&template_instantiation}); @@ -1821,12 +1828,6 @@ void translation_unit_visitor::process_template_specialization_argument( for (const auto &t : nested_template_instantiation->templates()) argument.add_template_param(t); - - // Check if this template should be simplified (e.g. system - // template aliases such as 'std:basic_string' should be - // simply 'std::string') - simplify_system_template(argument, - argument.to_string(config().using_namespace(), false)); } else if (arg.getAsType()->getAs() != nullptr) { @@ -1857,7 +1858,7 @@ void translation_unit_visitor::process_template_specialization_argument( } } - argument.set_name(type_name); + argument.set_type(type_name); } else { auto type_name = @@ -1879,7 +1880,7 @@ void translation_unit_visitor::process_template_specialization_argument( type_name.substr(0, type_name.find('<')); ensure_lambda_type_is_relative(unexposed_type_name); - argument.set_name(unexposed_type_name); + argument.set_type(unexposed_type_name); } else if (type_name.find("type-parameter-") == 0) { auto declaration_text = common::get_source_text_raw( @@ -1903,10 +1904,10 @@ void translation_unit_visitor::process_template_specialization_argument( // Otherwise just set the name for the template argument to // whatever clang says - argument.set_name(type_name); + argument.set_type(type_name); } else { - argument.set_name(type_name); + argument.set_type(type_name); } } @@ -1919,23 +1920,18 @@ void translation_unit_visitor::process_template_specialization_argument( template_instantiation.add_template(std::move(argument)); } else if (argument_kind == clang::TemplateArgument::Integral) { - template_parameter argument; - argument.is_template_parameter(false); - argument.set_type(std::to_string(arg.getAsIntegral().getExtValue())); + auto argument = template_parameter::make_argument( + std::to_string(arg.getAsIntegral().getExtValue())); template_instantiation.add_template(std::move(argument)); } else if (argument_kind == clang::TemplateArgument::Expression) { - template_parameter argument; - argument.is_template_parameter(false); - argument.set_type(common::get_source_text( - arg.getAsExpr()->getSourceRange(), source_manager())); + auto argument = + template_parameter::make_argument(common::get_source_text( + arg.getAsExpr()->getSourceRange(), source_manager())); template_instantiation.add_template(std::move(argument)); } else if (argument_kind == clang::TemplateArgument::TemplateExpansion) { - template_parameter argument; - argument.is_template_parameter(true); - - cls->getLocation().dump(source_manager()); + // TODO } else if (argument_kind == clang::TemplateArgument::Pack) { // This will only work for now if pack is at the end @@ -1973,17 +1969,18 @@ void translation_unit_visitor:: bool translation_unit_visitor::find_relationships_in_unexposed_template_params( const template_parameter &ct, found_relationships_t &relationships) { + assert(ct.type()); + bool found{false}; LOG_DBG("Finding relationships in user defined type: {}", ct.to_string(config().using_namespace(), false)); - // auto type_with_namespace = ctx.get_name_with_namespace(ct.type()); auto type_with_namespace = - std::make_optional(ct.type()); + std::make_optional(ct.type().value()); if (!type_with_namespace.has_value()) { // Couldn't find declaration of this type - type_with_namespace = common::model::namespace_{ct.type()}; + type_with_namespace = common::model::namespace_{ct.type().value()}; } auto element_opt = diagram().get(type_with_namespace.value().to_string()); @@ -2293,26 +2290,27 @@ void translation_unit_visitor:: auto arg_index = 0; for (const auto &arg : template_args) { const auto argument_kind = arg.getKind(); - template_parameter argument; + std::optional argument; if (argument_kind == clang::TemplateArgument::Template) { - build_template_instantiation_process_template_argument( - arg, argument); + argument = + build_template_instantiation_process_template_argument(arg); } else if (argument_kind == clang::TemplateArgument::Type) { - build_template_instantiation_process_type_argument(parent, - full_template_specialization_name, template_decl, arg, - template_instantiation, argument); + argument = build_template_instantiation_process_type_argument( + parent, full_template_specialization_name, template_decl, arg, + template_instantiation); } else if (argument_kind == clang::TemplateArgument::Integral) { - build_template_instantiation_process_integral_argument( - arg, argument); + argument = + build_template_instantiation_process_integral_argument(arg); } else if (argument_kind == clang::TemplateArgument::Expression) { - build_template_instantiation_process_expression_argument( - arg, argument); + argument = + build_template_instantiation_process_expression_argument(arg); } else { LOG_ERROR("Unsupported argument type {}", arg.getKind()); + continue; } // We can figure this only when we encounter variadic param in @@ -2326,43 +2324,42 @@ void translation_unit_visitor:: if (!template_base_params.empty()) { variadic_params = build_template_instantiation_add_base_classes( template_instantiation, template_base_params, arg_index, - variadic_params, argument); + variadic_params, argument.value()); } LOG_DBG("Adding template argument {} to template " "specialization/instantiation {}", - argument.name(), template_instantiation.name()); + argument.value().to_string(config().using_namespace(), false), + template_instantiation.name()); - simplify_system_template( - argument, argument.to_string(config().using_namespace(), false)); + simplify_system_template(argument.value(), + argument.value().to_string(config().using_namespace(), false)); - template_instantiation.add_template(std::move(argument)); + template_instantiation.add_template(std::move(argument.value())); arg_index++; } } -void translation_unit_visitor:: +template_parameter translation_unit_visitor:: build_template_instantiation_process_template_argument( - const clang::TemplateArgument &arg, template_parameter &argument) const + const clang::TemplateArgument &arg) const { - argument.is_template_parameter(true); auto arg_name = arg.getAsTemplate().getAsTemplateDecl()->getQualifiedNameAsString(); - argument.set_type(arg_name); + return template_parameter::make_template_type(arg_name); } -void translation_unit_visitor:: - build_template_instantiation_process_type_argument( - std::optional &parent, - const std::string &full_template_specialization_name, - const clang::TemplateDecl *template_decl, - const clang::TemplateArgument &arg, class_ &template_instantiation, - template_parameter &argument) +template_parameter +translation_unit_visitor::build_template_instantiation_process_type_argument( + std::optional &parent, + const std::string &full_template_specialization_name, + const clang::TemplateDecl *template_decl, + const clang::TemplateArgument &arg, class_ &template_instantiation) { assert(arg.getKind() == clang::TemplateArgument::Type); - argument.is_template_parameter(false); + auto argument = template_parameter::make_argument({}); // If this is a nested template type - add nested templates as // template arguments @@ -2414,12 +2411,11 @@ void translation_unit_visitor:: arg.getAsType()->getAs(); nested_template_type != nullptr) { - const auto nested_template_name = - nested_template_type->getTemplateName() - .getAsTemplateDecl() - ->getQualifiedNameAsString(); + const auto nested_type_name = nested_template_type->getTemplateName() + .getAsTemplateDecl() + ->getQualifiedNameAsString(); - argument.set_name(nested_template_name); + argument.set_type(nested_type_name); auto nested_template_instantiation = build_template_instantiation(*nested_template_type, @@ -2462,7 +2458,7 @@ void translation_unit_visitor:: } else if (arg.getAsType()->getAs() != nullptr) { argument.is_template_parameter(true); - argument.set_name( + argument.set_type( common::to_string(arg.getAsType(), template_decl->getASTContext())); } else { @@ -2471,26 +2467,26 @@ void translation_unit_visitor:: template_instantiation, full_template_specialization_name, template_decl, arg, argument); } + + return argument; } -void translation_unit_visitor:: +template_parameter translation_unit_visitor:: build_template_instantiation_process_integral_argument( - const clang::TemplateArgument &arg, template_parameter &argument) const + const clang::TemplateArgument &arg) const { assert(arg.getKind() == clang::TemplateArgument::Integral); - argument.is_template_parameter(false); - argument.set_type(std::to_string(arg.getAsIntegral().getExtValue())); + return template_parameter::make_argument( + std::to_string(arg.getAsIntegral().getExtValue())); } -void translation_unit_visitor:: +template_parameter translation_unit_visitor:: build_template_instantiation_process_expression_argument( - const clang::TemplateArgument &arg, template_parameter &argument) const + const clang::TemplateArgument &arg) const { assert(arg.getKind() == clang::TemplateArgument::Expression); - - argument.is_template_parameter(false); - argument.set_type(common::get_source_text( + return template_parameter::make_argument(common::get_source_text( arg.getAsExpr()->getSourceRange(), source_manager())); } @@ -2505,7 +2501,7 @@ void translation_unit_visitor:: argument.is_template_parameter(false); - argument.set_name( + argument.set_type( common::to_string(arg.getAsType(), template_decl->getASTContext())); if (const auto *record_type = arg.getAsType()->getAs(); @@ -2688,7 +2684,7 @@ void translation_unit_visitor::process_field( for (const auto &template_argument : template_specialization.templates()) { - LOG_DBG("Looking for nested relationships from {}:{} in " + LOG_DBG("Looking for nested relationships from {}::{} in " "template {}", c.full_name(false), field_name, template_argument.to_string( @@ -2792,7 +2788,7 @@ bool translation_unit_visitor::simplify_system_template( template_parameter &ct, const std::string &full_name) const { if (config().type_aliases().count(full_name) > 0) { - ct.set_name(config().type_aliases().at(full_name)); + ct.set_type(config().type_aliases().at(full_name)); ct.clear_params(); return true; } diff --git a/src/class_diagram/visitor/translation_unit_visitor.h b/src/class_diagram/visitor/translation_unit_visitor.h index 22cbf048..e18573a8 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.h +++ b/src/class_diagram/visitor/translation_unit_visitor.h @@ -218,25 +218,22 @@ private: const clang::TemplateArgument &arg, common::model::template_parameter &argument); - void build_template_instantiation_process_expression_argument( - const clang::TemplateArgument &arg, - common::model::template_parameter &argument) const; + template_parameter build_template_instantiation_process_expression_argument( + const clang::TemplateArgument &arg) const; - void build_template_instantiation_process_integral_argument( - const clang::TemplateArgument &arg, - common::model::template_parameter &argument) const; + template_parameter build_template_instantiation_process_integral_argument( + const clang::TemplateArgument &arg) const; - void build_template_instantiation_process_type_argument( + template_parameter build_template_instantiation_process_type_argument( std::optional &parent, const std::string &full_template_specialization_name, const clang::TemplateDecl *template_decl, const clang::TemplateArgument &arg, - model::class_ &template_instantiation, - common::model::template_parameter &argument); + model::class_ &template_instantiation); - void build_template_instantiation_process_template_argument( - const clang::TemplateArgument &arg, - common::model::template_parameter &argument) const; + common::model::template_parameter + build_template_instantiation_process_template_argument( + const clang::TemplateArgument &arg) const; void ensure_lambda_type_is_relative(std::string ¶meter_type) const; diff --git a/src/common/clang_utils.cc b/src/common/clang_utils.cc index 543a40b3..f9c42fcf 100644 --- a/src/common/clang_utils.cc +++ b/src/common/clang_utils.cc @@ -368,8 +368,14 @@ std::vector parse_unexposed_template_params( nested_params = parse_unexposed_template_params( nested_params_str, ns_resolve, depth + 1); - if (nested_params.empty()) - nested_params.emplace_back(nested_params_str); + if (nested_params.empty()) { + // We couldn't extract any nested template parameters from + // `nested_params_str` so just add it as type of template + // argument as is + nested_params.emplace_back( + template_parameter::make_unexposed_argument( + nested_params_str)); + } it = bracket_match_end - 1; } @@ -386,8 +392,8 @@ std::vector parse_unexposed_template_params( type += *it; } if (complete_class_template_argument) { - template_parameter t; - t.set_type(ns_resolve(clanguml::util::trim(type))); + auto t = template_parameter::make_unexposed_argument( + ns_resolve(clanguml::util::trim(type))); type = ""; for (auto &¶m : nested_params) t.add_template_param(std::move(param)); @@ -399,8 +405,8 @@ std::vector parse_unexposed_template_params( } if (!type.empty()) { - template_parameter t; - t.set_type(ns_resolve(clanguml::util::trim(type))); + auto t = template_parameter::make_unexposed_argument( + ns_resolve(clanguml::util::trim(type))); type = ""; for (auto &¶m : nested_params) t.add_template_param(std::move(param)); diff --git a/src/common/model/template_parameter.cc b/src/common/model/template_parameter.cc index 61e58d30..f7c4aca5 100644 --- a/src/common/model/template_parameter.cc +++ b/src/common/model/template_parameter.cc @@ -23,18 +23,38 @@ #include namespace clanguml::common::model { - -template_parameter::template_parameter(const std::string &type, - const std::string &name, std::string default_value, bool is_variadic) - : default_value_{std::move(default_value)} - , is_variadic_{is_variadic} +std::string to_string(template_parameter_kind_t k) { - set_name(name); - set_type(type); + switch (k) { + case template_parameter_kind_t::template_type: + return "template_type"; + case template_parameter_kind_t::template_template_type: + return "template_template_type"; + case template_parameter_kind_t::non_type_template: + return "non_type_template"; + case template_parameter_kind_t::argument: + return "argument"; + case template_parameter_kind_t::concept_constraint: + return "concept_constraint"; + default: + assert(0); + } } +// template_parameter::template_parameter(const std::optional +// &type, +// const std::optional &name, +// const std::optional &default_value, bool is_variadic) +// : type_{type} +// , name_{name} +// , default_value_{std::move(default_value)} +// , is_variadic_{is_variadic} +//{ +// } void template_parameter::set_type(const std::string &type) { + assert(kind_ != template_parameter_kind_t::template_type); + if (util::ends_with(type, std::string{"..."})) { type_ = type.substr(0, type.size() - 3); is_variadic_ = true; @@ -43,16 +63,24 @@ void template_parameter::set_type(const std::string &type) type_ = type; } -std::string template_parameter::type() const +std::optional template_parameter::type() const { - if (is_variadic_ && !type_.empty()) - return type_ + "..."; + if (!type_) + return {}; + + if (is_variadic_) + return type_.value() + "..."; return type_; } void template_parameter::set_name(const std::string &name) { + assert(kind_ != template_parameter_kind_t::argument); + + if (name.empty()) + return; + if (util::ends_with(name, std::string{"..."})) { name_ = name.substr(0, name.size() - 3); is_variadic_ = true; @@ -61,10 +89,13 @@ void template_parameter::set_name(const std::string &name) name_ = name; } -std::string template_parameter::name() const +std::optional template_parameter::name() const { - if (is_variadic_ && type_.empty()) - return name_ + "..."; + if (!name_) + return {}; + + if (is_variadic_ && (kind_ != template_parameter_kind_t::non_type_template)) + return name_.value() + "..."; return name_; } @@ -74,7 +105,10 @@ void template_parameter::set_default_value(const std::string &value) default_value_ = value; } -std::string template_parameter::default_value() const { return default_value_; } +const std::optional &template_parameter::default_value() const +{ + return default_value_; +} void template_parameter::is_variadic(bool is_variadic) noexcept { @@ -138,14 +172,17 @@ std::string template_parameter::to_string( { using clanguml::common::model::namespace_; - assert(!(!type().empty() && concept_constraint().has_value())); + assert(!(type().has_value() && concept_constraint().has_value())); std::string res; - if (!type().empty()) { + const auto maybe_type = type(); + if (maybe_type) { if (!relative) - res += namespace_{type()}.to_string(); + res += namespace_{*maybe_type}.to_string(); else - res += namespace_{type()}.relative_to(using_namespace).to_string(); + res += namespace_{*maybe_type} + .relative_to(using_namespace) + .to_string(); } const auto &maybe_concept_constraint = concept_constraint(); @@ -159,14 +196,19 @@ std::string template_parameter::to_string( .to_string(); } - if (!name().empty()) { - if (!type().empty() || maybe_concept_constraint) + const auto maybe_name = name(); + + if (maybe_name) { + if ((maybe_type && !maybe_type.value().empty()) || + maybe_concept_constraint) res += " "; if (!relative) - res += namespace_{name()}.to_string(); + res += namespace_{*maybe_name}.to_string(); else - res += namespace_{name()}.relative_to(using_namespace).to_string(); + res += namespace_{*maybe_name} + .relative_to(using_namespace) + .to_string(); } // Render nested template params @@ -181,9 +223,10 @@ std::string template_parameter::to_string( res += fmt::format("<{}>", fmt::join(params, ",")); } - if (!default_value().empty()) { + const auto &maybe_default_value = default_value(); + if (maybe_default_value) { res += "="; - res += default_value(); + res += maybe_default_value.value(); } return res; @@ -200,7 +243,8 @@ bool template_parameter::find_nested_relationships( // If this type argument should be included in the relationship // just add it and skip recursion (e.g. this is a user defined type) - if (should_include(name())) { + const auto maybe_type = type(); + if (maybe_type && should_include(maybe_type.value())) { const auto maybe_id = id(); if (maybe_id) { nested_relationships.emplace_back(maybe_id.value(), hint); @@ -212,8 +256,11 @@ bool template_parameter::find_nested_relationships( // interested what is stored inside it else { for (const auto &template_argument : template_params()) { + const auto maybe_id = template_argument.id(); - if (should_include(template_argument.name()) && maybe_id) { + const auto maybe_arg_type = template_argument.type(); + + if (maybe_id && maybe_arg_type && should_include(*maybe_arg_type)) { nested_relationships.emplace_back(maybe_id.value(), hint); diff --git a/src/common/model/template_parameter.h b/src/common/model/template_parameter.h index b9ce70eb..7ebba8de 100644 --- a/src/common/model/template_parameter.h +++ b/src/common/model/template_parameter.h @@ -26,30 +26,105 @@ namespace clanguml::common::model { -/// @brief Represents template parameter or template argument +enum class template_parameter_kind_t { + template_type, + template_template_type, + non_type_template, + argument, // a.k.a. type parameter specialization + concept_constraint +}; + +std::string to_string(template_parameter_kind_t k); + +/// @brief Represents template parameter, template arguments or concept +/// constraints /// /// This class can represent both template parameter and template arguments, /// including variadic parameters and instantiations with /// nested templates class template_parameter { -public: - template_parameter(const std::string &type = "", - const std::string &name = "", std::string default_value = "", - bool is_variadic = false); + template_parameter() = default; - template_parameter(const template_parameter &right) = default; +public: + static template_parameter make_template_type(std::string name, + const std::optional &default_value = {}, + bool is_variadic = false) + { + template_parameter p; + p.set_kind(template_parameter_kind_t::template_type); + p.set_name(std::move(name)); + p.is_variadic(is_variadic); + if (default_value) + p.set_default_value(default_value.value()); + return p; + } + + static template_parameter make_template_template_type(std::string name, + const std::optional &default_value = {}, + bool is_variadic = false) + { + template_parameter p; + p.set_kind(template_parameter_kind_t::template_template_type); + p.set_name(name + "<>"); + if (default_value) + p.set_default_value(default_value.value()); + p.is_variadic(is_variadic); + return p; + } + + static template_parameter make_non_type_template(std::string type, + const std::optional &name, + const std::optional &default_value = {}, + bool is_variadic = false) + { + template_parameter p; + p.set_kind(template_parameter_kind_t::non_type_template); + p.set_type(std::move(type)); + if (name) + p.set_name(name.value()); + if (default_value) + p.set_default_value(default_value.value()); + p.is_variadic(is_variadic); + return p; + } + + static template_parameter make_argument( + std::string type, const std::optional &default_value = {}) + { + template_parameter p; + p.set_kind(template_parameter_kind_t::argument); + p.set_type(std::move(type)); + if (default_value) + p.set_default_value(default_value.value()); + return p; + } + + static template_parameter make_unexposed_argument( + std::string type, const std::optional &default_value = {}) + { + template_parameter p = make_argument(std::move(type), default_value); + p.set_unexposed(true); + return p; + } + + // template_parameter(const std::optional &type = {}, + // const std::optional &name = {}, + // const std::optional &default_value = {}, + // bool is_variadic = false); + + // template_parameter(const template_parameter &right) = default; void set_type(const std::string &type); - std::string type() const; + std::optional type() const; void set_id(const int64_t id) { id_ = id; } - std::optional id() const { return id_; } + const std::optional &id() const { return id_; } void set_name(const std::string &name); - std::string name() const; + std::optional name() const; void set_default_value(const std::string &value); - std::string default_value() const; + const std::optional &default_value() const; void is_variadic(bool is_variadic) noexcept; bool is_variadic() const noexcept; @@ -101,18 +176,29 @@ public: const; void set_concept_constraint(std::string constraint); + const std::optional &concept_constraint() const; + template_parameter_kind_t kind() const { return kind_; } + + void set_kind(template_parameter_kind_t kind) { kind_ = kind; } + + bool is_unexposed() const { return is_unexposed_; } + + void set_unexposed(bool unexposed) { is_unexposed_ = unexposed; } + private: + template_parameter_kind_t kind_; + /// Represents the type of non-type template parameters - /// e.g. 'int' - std::string type_; + /// e.g. 'int' or type of template arguments + std::optional type_; /// The name of the parameter (e.g. 'T' or 'N') - std::string name_; + std::optional name_; /// Default value of the template parameter - std::string default_value_; + std::optional default_value_; /// Whether the template parameter is a regular template parameter /// When false, it is a non-type template parameter @@ -136,5 +222,7 @@ private: std::vector template_params_; std::optional id_; + + bool is_unexposed_{false}; }; } // namespace clanguml::common::model diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index 758afcfc..6630c391 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -1353,12 +1353,14 @@ bool translation_unit_visitor::process_template_parameters( nullptr) { const auto *template_type_parameter = clang::dyn_cast_or_null(parameter); - template_parameter ct; - ct.set_type(""); - ct.is_template_parameter(true); - ct.set_name(template_type_parameter->getNameAsString()); - ct.set_default_value(""); - ct.is_variadic(template_type_parameter->isParameterPack()); + std::optional default_arg; + if (template_type_parameter->hasDefaultArgument()) { + default_arg = + template_type_parameter->getDefaultArgument().getAsString(); + } + auto ct = template_parameter::make_template_type( + template_type_parameter->getNameAsString(), default_arg, + template_type_parameter->isParameterPack()); c.add_template(std::move(ct)); } @@ -1367,12 +1369,13 @@ bool translation_unit_visitor::process_template_parameters( const auto *template_nontype_parameter = clang::dyn_cast_or_null( parameter); - template_parameter ct; - ct.set_type(template_nontype_parameter->getType().getAsString()); - ct.set_name(template_nontype_parameter->getNameAsString()); - ct.is_template_parameter(false); - ct.set_default_value(""); - ct.is_variadic(template_nontype_parameter->isParameterPack()); + std::optional default_arg; + if (template_nontype_parameter->hasDefaultArgument()) + default_arg = common::to_string( + template_nontype_parameter->getDefaultArgument()); + auto ct = template_parameter::make_non_type_template( + template_nontype_parameter->getType().getAsString(), + template_nontype_parameter->getNameAsString(), default_arg); c.add_template(std::move(ct)); } @@ -1381,12 +1384,15 @@ bool translation_unit_visitor::process_template_parameters( const auto *template_template_parameter = clang::dyn_cast_or_null( parameter); - template_parameter ct; - ct.set_type(""); - ct.set_name(template_template_parameter->getNameAsString() + "<>"); - ct.is_template_parameter(true); - ct.set_default_value(""); - ct.is_variadic(template_template_parameter->isParameterPack()); + std::optional default_arg; + if (template_template_parameter->hasDefaultArgument()) + default_arg = common::to_string( + template_template_parameter->getDefaultArgument() + .getArgument() + .getAsExpr()); + auto ct = template_parameter::make_template_template_type( + template_template_parameter->getNameAsString(), default_arg, + template_template_parameter->isParameterPack()); c.add_template(std::move(ct)); } @@ -1513,66 +1519,61 @@ void translation_unit_visitor:: { for (const auto &arg : template_args) { const auto argument_kind = arg.getKind(); - common::model::template_parameter argument; + std::optional argument; if (argument_kind == clang::TemplateArgument::Template) { - build_template_instantiation_process_template_argument( - arg, argument); + argument = + build_template_instantiation_process_template_argument(arg); } else if (argument_kind == clang::TemplateArgument::Type) { - build_template_instantiation_process_type_argument(parent, - full_template_specialization_name, template_decl, arg, - template_instantiation, argument); + argument = build_template_instantiation_process_type_argument( + parent, full_template_specialization_name, template_decl, arg, + template_instantiation); } else if (argument_kind == clang::TemplateArgument::Integral) { - build_template_instantiation_process_integral_argument( - arg, argument); + argument = + build_template_instantiation_process_integral_argument(arg); } else if (argument_kind == clang::TemplateArgument::Expression) { - build_template_instantiation_process_expression_argument( - arg, argument); + argument = + build_template_instantiation_process_expression_argument(arg); } else { LOG_INFO("Unsupported argument type {}", arg.getKind()); + continue; } - simplify_system_template( - argument, argument.to_string(config().using_namespace(), false)); + simplify_system_template(argument.value(), + argument.value().to_string(config().using_namespace(), false)); - template_instantiation.add_template(std::move(argument)); + template_instantiation.add_template(std::move(argument.value())); } } -void translation_unit_visitor:: +template_parameter translation_unit_visitor:: build_template_instantiation_process_template_argument( - const clang::TemplateArgument &arg, - common::model::template_parameter &argument) const + const clang::TemplateArgument &arg) const { - argument.is_template_parameter(true); auto arg_name = arg.getAsTemplate().getAsTemplateDecl()->getQualifiedNameAsString(); - argument.set_type(arg_name); + return template_parameter::make_template_type(arg_name); } -void translation_unit_visitor:: +template_parameter translation_unit_visitor:: build_template_instantiation_process_integral_argument( - const clang::TemplateArgument &arg, - common::model::template_parameter &argument) const + const clang::TemplateArgument &arg) const { assert(arg.getKind() == clang::TemplateArgument::Integral); - argument.is_template_parameter(false); - argument.set_type(std::to_string(arg.getAsIntegral().getExtValue())); + return template_parameter::make_argument( + std::to_string(arg.getAsIntegral().getExtValue())); } -void translation_unit_visitor:: +template_parameter translation_unit_visitor:: build_template_instantiation_process_expression_argument( - const clang::TemplateArgument &arg, - common::model::template_parameter &argument) const + const clang::TemplateArgument &arg) const { assert(arg.getKind() == clang::TemplateArgument::Expression); - - argument.is_template_parameter(false); - argument.set_type(common::get_source_text( + return template_parameter::make_argument(common::get_source_text( arg.getAsExpr()->getSourceRange(), source_manager())); } @@ -1592,17 +1593,18 @@ void translation_unit_visitor:: common::to_string(arg.getAsType(), template_decl->getASTContext())); } -void translation_unit_visitor:: - build_template_instantiation_process_type_argument( - model::template_trait * /*parent*/, - const std::string &full_template_specialization_name, - const clang::TemplateDecl *template_decl, - const clang::TemplateArgument &arg, - model::template_trait &template_instantiation, - common::model::template_parameter &argument) +common::model::template_parameter +translation_unit_visitor::build_template_instantiation_process_type_argument( + model::template_trait * /*parent*/, + const std::string &full_template_specialization_name, + const clang::TemplateDecl *template_decl, + const clang::TemplateArgument &arg, + model::template_trait &template_instantiation) { assert(arg.getKind() == clang::TemplateArgument::Type); + auto argument = template_parameter::make_argument({}); + argument.is_template_parameter(false); // If this is a nested template type - add nested templates as @@ -1619,7 +1621,7 @@ void translation_unit_visitor:: .getAsTemplateDecl() ->getQualifiedNameAsString(); - argument.set_name(nested_template_name); + argument.set_type(nested_template_name); // Check if this template should be simplified (e.g. system // template aliases such as 'std:basic_string' should @@ -1629,7 +1631,7 @@ void translation_unit_visitor:: } else if (arg.getAsType()->getAs() != nullptr) { argument.is_template_parameter(true); - argument.set_name( + argument.set_type( common::to_string(arg.getAsType(), template_decl->getASTContext())); } else { @@ -1638,6 +1640,8 @@ void translation_unit_visitor:: template_instantiation, full_template_specialization_name, template_decl, arg, argument); } + + return argument; } std::unique_ptr @@ -1688,7 +1692,7 @@ void translation_unit_visitor::process_template_specialization_argument( const auto argument_kind = arg.getKind(); if (argument_kind == clang::TemplateArgument::Type) { - common::model::template_parameter argument; + auto argument = template_parameter::make_argument({}); argument.is_template_parameter(false); // If this is a nested template type - add nested templates as @@ -1817,23 +1821,18 @@ void translation_unit_visitor::process_template_specialization_argument( template_instantiation.add_template(std::move(argument)); } else if (argument_kind == clang::TemplateArgument::Integral) { - common::model::template_parameter argument; - argument.is_template_parameter(false); - argument.set_type(std::to_string(arg.getAsIntegral().getExtValue())); + auto argument = template_parameter::make_argument( + std::to_string(arg.getAsIntegral().getExtValue())); template_instantiation.add_template(std::move(argument)); } else if (argument_kind == clang::TemplateArgument::Expression) { - common::model::template_parameter argument; - argument.is_template_parameter(false); - argument.set_type(common::get_source_text( - arg.getAsExpr()->getSourceRange(), source_manager())); + auto argument = + template_parameter::make_argument(common::get_source_text( + arg.getAsExpr()->getSourceRange(), source_manager())); template_instantiation.add_template(std::move(argument)); } else if (argument_kind == clang::TemplateArgument::TemplateExpansion) { - common::model::template_parameter argument; - argument.is_template_parameter(true); - - cls->getLocation().dump(source_manager()); + // TODO } else if (argument_kind == clang::TemplateArgument::Pack) { // This will only work for now if pack is at the end diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.h b/src/sequence_diagram/visitor/translation_unit_visitor.h index 9429c5eb..1ba9d80c 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.h +++ b/src/sequence_diagram/visitor/translation_unit_visitor.h @@ -202,17 +202,17 @@ private: const std::string &full_template_specialization_name, const clang::TemplateDecl *template_decl); - void build_template_instantiation_process_template_argument( - const clang::TemplateArgument &arg, - common::model::template_parameter &argument) const; + common::model::template_parameter + build_template_instantiation_process_template_argument( + const clang::TemplateArgument &arg) const; - void build_template_instantiation_process_integral_argument( - const clang::TemplateArgument &arg, - common::model::template_parameter &argument) const; + common::model::template_parameter + build_template_instantiation_process_integral_argument( + const clang::TemplateArgument &arg) const; - void build_template_instantiation_process_expression_argument( - const clang::TemplateArgument &arg, - common::model::template_parameter &argument) const; + common::model::template_parameter + build_template_instantiation_process_expression_argument( + const clang::TemplateArgument &arg) const; void build_template_instantiation_process_tag_argument( model::template_trait &template_instantiation, @@ -221,13 +221,12 @@ private: const clang::TemplateArgument &arg, common::model::template_parameter &argument) const; - void build_template_instantiation_process_type_argument( + common::model::template_parameter build_template_instantiation_process_type_argument( model::template_trait *parent, const std::string &full_template_specialization_name, const clang::TemplateDecl *template_decl, const clang::TemplateArgument &arg, - model::template_trait &template_instantiation, - common::model::template_parameter &argument); + model::template_trait &template_instantiation); std::unique_ptr process_template_specialization( clang::ClassTemplateSpecializationDecl *cls); diff --git a/tests/t00008/test_case.h b/tests/t00008/test_case.h index 446541d0..aad94ba6 100644 --- a/tests/t00008/test_case.h +++ b/tests/t00008/test_case.h @@ -36,7 +36,7 @@ TEST_CASE("t00008", "[test-case][class]") // TODO: add option to resolve using declared types // REQUIRE_THAT(puml, IsClassTemplate("A", "T, P, bool (*)(int, int), int // N")); - REQUIRE_THAT(puml, IsClassTemplate("A", "T,P,CMP,int N")); + REQUIRE_THAT(puml, IsClassTemplate("A", "T,P=T,CMP=nullptr,int N=3")); REQUIRE_THAT(puml, IsClassTemplate("B", "T,C<>")); REQUIRE_THAT(puml, (IsField("value", "T"))); diff --git a/tests/test_util.cc b/tests/test_util.cc index 28905eaa..2a1b26de 100644 --- a/tests/test_util.cc +++ b/tests/test_util.cc @@ -105,8 +105,8 @@ TEST_CASE("Test parse_unexposed_template_params", "[unit-test]") CHECK(int_template.size() == 1); CHECK(int_template[0].template_params().size() == 1); - CHECK(int_template[0].type() == "ns1::ns2::class1"); - CHECK(int_template[0].template_params()[0].type() == "int"); + CHECK(int_template[0].type().value() == "ns1::ns2::class1"); + CHECK(int_template[0].template_params()[0].type().value() == "int"); const std::string int_int_template_str{"ns1::ns2::class1"}; @@ -115,9 +115,9 @@ TEST_CASE("Test parse_unexposed_template_params", "[unit-test]") CHECK(int_int_template.size() == 1); CHECK(int_int_template[0].template_params().size() == 2); - CHECK(int_int_template[0].type() == "ns1::ns2::class1"); - CHECK(int_int_template[0].template_params()[0].type() == "int"); - CHECK(int_int_template[0].template_params()[1].type() == "int"); + CHECK(int_int_template[0].type().value() == "ns1::ns2::class1"); + CHECK(int_int_template[0].template_params()[0].type().value() == "int"); + CHECK(int_int_template[0].template_params()[1].type().value() == "int"); const std::string nested_template_str{ "class1>>"}; @@ -127,13 +127,13 @@ TEST_CASE("Test parse_unexposed_template_params", "[unit-test]") CHECK(nested_template.size() == 1); CHECK(nested_template[0].template_params().size() == 2); - CHECK(nested_template[0].type() == "class1"); - CHECK(nested_template[0].template_params()[0].type() == "int"); + CHECK(nested_template[0].type().value() == "class1"); + CHECK(nested_template[0].template_params()[0].type().value() == "int"); const auto &class2 = nested_template[0].template_params()[1]; CHECK(class2.type() == "ns1::class2"); - CHECK(class2.template_params()[0].type() == "int"); - CHECK(class2.template_params()[1].type() == "std::vector"); - CHECK(class2.template_params()[1].template_params()[0].type() == + CHECK(class2.template_params()[0].type().value() == "int"); + CHECK(class2.template_params()[1].type().value() == "std::vector"); + CHECK(class2.template_params()[1].template_params()[0].type().value() == "std::string"); const std::string empty_string = R"( @@ -153,7 +153,7 @@ TEST_CASE("Test parse_unexposed_template_params", "[unit-test]") single_template_string, [](const auto &n) { return n; }); CHECK(single_template.size() == 1); - CHECK(single_template[0].type() == "Else"); + CHECK(single_template[0].type().value() == "Else"); const std::string declaration_string = R"( @@ -165,9 +165,9 @@ TEST_CASE("Test parse_unexposed_template_params", "[unit-test]") declaration_string, [](const auto &n) { return n; }); CHECK(declaration_template.size() == 3); - CHECK(declaration_template[0].type() == "std::true_type"); - CHECK(declaration_template[1].type() == "Result"); - CHECK(declaration_template[2].type() == "Tail"); + CHECK(declaration_template[0].type().value() == "std::true_type"); + CHECK(declaration_template[1].type().value() == "Result"); + CHECK(declaration_template[2].type().value() == "Tail"); } TEST_CASE("Test remove_prefix", "[unit-test]")