From 1cf3ceff7b0a9b5c6fba78c4348c425c0954ad6f Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Thu, 25 Aug 2022 13:53:13 +0200 Subject: [PATCH] Refactored build_template_instantiation method --- .../visitor/translation_unit_visitor.cc | 696 +++++++----------- .../visitor/translation_unit_visitor.h | 36 + src/common/clang_utils.cc | 16 + 3 files changed, 320 insertions(+), 428 deletions(-) diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index e0d1bf7b..9b07e1b5 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -402,22 +402,6 @@ bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls) set_ast_local_id(cls->getID(), cls_id); - // if (cls->getQualifiedNameAsString() == - // "cppast::cpp_function_parameter" && - // cls->getLocation().printToString(source_manager_) == - // "/home/bartek/devel/clang-uml-showcases/cppast/src/../include/" - // "cppast/cpp_function.hpp:16:7") { - // LOG_DBG("##############################################################" - // "##########################"); - // for (const auto &c : diagram().classes()) { - // LOG_DBG(" >> {} [{}]", c.get().full_name(false), - // c.get().id()); - // } - // const auto &ccc = diagram().get_class(cls_id); - // if (ccc.has_value()) - // LOG_DBG("---------- {}", ccc.get()->full_name(false)); - // } - // Templated records are handled by VisitClassTemplateDecl() if (cls->isTemplated() || cls->isTemplateDecl() || (clang::dyn_cast_or_null(cls) != @@ -1368,6 +1352,14 @@ std::unique_ptr translation_unit_visitor:: auto template_instantiation_ptr = std::make_unique(config_.using_namespace()); + // + // Here we'll hold the template base params to replace with the + // instantiated values + // + std::deque> + template_base_params{}; + auto &template_instantiation = *template_instantiation_ptr; std::string full_template_specialization_name = to_string(record_type, template_specialization.getASTContext()); @@ -1384,207 +1376,11 @@ std::unique_ptr translation_unit_visitor:: template_instantiation.set_id(template_decl->getID() + (std::hash{}(full_template_specialization_name) >> 4)); - [[maybe_unused]] auto arg_index = 0U; - for (const auto &arg : - template_specialization.getTemplateArgs().asArray()) { - const auto argument_kind = arg.getKind(); - template_parameter argument; - if (argument_kind == clang::TemplateArgument::ArgKind::Template) { - argument.is_template_parameter(true); - auto arg_name = arg.getAsTemplate() - .getAsTemplateDecl() - ->getQualifiedNameAsString(); - argument.set_type(arg_name); - } - else if (argument_kind == clang::TemplateArgument::ArgKind::Type) { - argument.is_template_parameter(false); - - // If this is a nested template type - add nested templates as - // template arguments - if (arg.getAsType()->getAs()) { - - for (const auto ¶m_type : - arg.getAsType() - ->getAs() - ->param_types()) { - - if (!param_type->getAs()) - continue; - - auto classTemplateSpecialization = - llvm::dyn_cast( - param_type->getAsRecordDecl()); - - if (classTemplateSpecialization) { - // Read arg info as needed. - auto nested_template_instantiation = - build_template_instantiation_from_class_template_specialization( - *classTemplateSpecialization, - *param_type->getAs(), - diagram().should_include( - full_template_specialization_name) - ? std::make_optional( - &template_instantiation) - : parent); - - const auto nested_template_name = - classTemplateSpecialization - ->getQualifiedNameAsString(); - - auto [tinst_ns, tinst_name] = - cx::util::split_ns(nested_template_name); - - if (nested_template_instantiation && - diagram().should_include( - full_template_specialization_name)) { - if (diagram().should_include( - tinst_ns, tinst_name)) { - template_instantiation.add_relationship( - {relationship_t::kDependency, - nested_template_instantiation->id()}); - } - else { - if (parent.has_value()) - parent.value()->add_relationship( - {relationship_t::kDependency, - nested_template_instantiation - ->id()}); - } - } - } - } - } - else if (arg.getAsType() - ->getAs()) { - const auto *nested_template_type = - arg.getAsType()->getAs(); - - const auto nested_template_name = - nested_template_type->getTemplateName() - .getAsTemplateDecl() - ->getQualifiedNameAsString(); - - auto [tinst_ns, tinst_name] = - cx::util::split_ns(nested_template_name); - - argument.set_name(nested_template_name); - - auto nested_template_instantiation = - build_template_instantiation( - *arg.getAsType() - ->getAs(), - diagram().should_include( - full_template_specialization_name) - ? std::make_optional(&template_instantiation) - : parent); - - argument.set_id(nested_template_instantiation->id()); - - 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)); - - if (nested_template_instantiation && - diagram().should_include( - nested_template_instantiation->full_name(false))) { - if (diagram().should_include( - full_template_specialization_name)) { - template_instantiation.add_relationship( - {relationship_t::kDependency, - nested_template_instantiation->id()}); - } - else { - 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)); - } - } - else if (arg.getAsType()->getAs()) { - argument.is_template_parameter(true); - argument.set_name( - to_string(arg.getAsType(), template_decl->getASTContext())); - } - else { - // This is just a regular type - argument.is_template_parameter(false); - - argument.set_name( - to_string(arg.getAsType(), template_decl->getASTContext())); - - if (arg.getAsType()->getAs() && - arg.getAsType() - ->getAs() - ->getAsRecordDecl()) { - argument.set_id( - common::to_id(*arg.getAsType() - ->getAs() - ->getAsRecordDecl())); - - if (diagram().should_include( - full_template_specialization_name)) { - // Add dependency relationship to the parent - // template - template_instantiation.add_relationship( - {relationship_t::kDependency, - common::to_id(*arg.getAsType() - ->getAs() - ->getAsRecordDecl())}); - } - } - else if (arg.getAsType()->getAs()) { - if (arg.getAsType() - ->getAs() - ->getAsTagDecl()) { - template_instantiation.add_relationship( - {relationship_t::kDependency, - common::to_id(*arg.getAsType() - ->getAs() - ->getAsTagDecl())}); - } - } - } - } - else if (argument_kind == clang::TemplateArgument::ArgKind::Integral) { - argument.is_template_parameter(false); - argument.set_type( - std::to_string(arg.getAsIntegral().getExtValue())); - } - else if (argument_kind == - clang::TemplateArgument::ArgKind::Expression) { - argument.is_template_parameter(false); - argument.set_type(get_source_text( - arg.getAsExpr()->getSourceRange(), source_manager_)); - } - else { - LOG_ERROR("Unsupported argument type {}", arg.getKind()); - } - - LOG_DBG("Adding template argument {} to template " - "specialization/instantiation {}", - argument.name(), template_instantiation.name()); - - simplify_system_template( - argument, argument.to_string(config().using_namespace(), false)); - - template_instantiation.add_template(std::move(argument)); - - arg_index++; - } + build_template_instantiation_process_template_arguments(parent, + template_base_params, + template_specialization.getTemplateArgs().asArray(), + template_instantiation, full_template_specialization_name, + template_decl); // First try to find the best match for this template in partially // specialized templates @@ -1739,217 +1535,10 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( base_index++; } - // TODO: Refactor handling of template arguments to a separate method - auto arg_index = 0U; - for (const auto &arg : template_type) { - const auto argument_kind = arg.getKind(); - template_parameter argument; - if (argument_kind == clang::TemplateArgument::ArgKind::Template) { - argument.is_template_parameter(true); - auto arg_name = arg.getAsTemplate() - .getAsTemplateDecl() - ->getQualifiedNameAsString(); - argument.set_type(arg_name); - } - else if (argument_kind == clang::TemplateArgument::ArgKind::Type) { - argument.is_template_parameter(false); - - // If this is a nested template type - add nested templates as - // template arguments - if (arg.getAsType()->getAs()) { - - for (const auto ¶m_type : - arg.getAsType() - ->getAs() - ->param_types()) { - - if (!param_type->getAs()) - continue; - - auto classTemplateSpecialization = - llvm::dyn_cast( - param_type->getAsRecordDecl()); - - if (classTemplateSpecialization) { - // Read arg info as needed. - auto nested_template_instantiation = - build_template_instantiation_from_class_template_specialization( - *classTemplateSpecialization, - *param_type->getAs(), - diagram().should_include( - full_template_specialization_name) - ? std::make_optional( - &template_instantiation) - : parent); - - const auto nested_template_name = - classTemplateSpecialization - ->getQualifiedNameAsString(); - - auto [tinst_ns, tinst_name] = - cx::util::split_ns(nested_template_name); - - 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)); - } - } - } - } - else if (arg.getAsType() - ->getAs()) { - const auto *nested_template_type = - arg.getAsType()->getAs(); - - const auto nested_template_name = - nested_template_type->getTemplateName() - .getAsTemplateDecl() - ->getQualifiedNameAsString(); - - auto [tinst_ns, tinst_name] = - cx::util::split_ns(nested_template_name); - - argument.set_name(nested_template_name); - - auto nested_template_instantiation = - build_template_instantiation( - *arg.getAsType() - ->getAs(), - diagram().should_include( - full_template_specialization_name) - ? std::make_optional(&template_instantiation) - : parent); - - argument.set_id(nested_template_instantiation->id()); - - 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)); - - if (nested_template_instantiation && - diagram().should_include( - nested_template_instantiation->full_name(false))) { - if (diagram().should_include( - full_template_specialization_name)) { - template_instantiation.add_relationship( - {relationship_t::kDependency, - nested_template_instantiation->id()}); - } - else { - 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)); - } - } - else if (arg.getAsType()->getAs()) { - argument.is_template_parameter(true); - argument.set_name( - to_string(arg.getAsType(), template_decl->getASTContext())); - } - else { - // This is just a regular type - argument.is_template_parameter(false); - - argument.set_name( - to_string(arg.getAsType(), template_decl->getASTContext())); - - if (arg.getAsType()->getAs() && - arg.getAsType() - ->getAs() - ->getAsRecordDecl()) { - argument.set_id( - common::to_id(*arg.getAsType() - ->getAs() - ->getAsRecordDecl())); - - if (diagram().should_include( - full_template_specialization_name)) { - // Add dependency relationship to the parent - // template - template_instantiation.add_relationship( - {relationship_t::kDependency, - common::to_id(*arg.getAsType() - ->getAs() - ->getAsRecordDecl())}); - } - } - else if (arg.getAsType()->getAs()) { - if (arg.getAsType() - ->getAs() - ->getAsTagDecl()) { - template_instantiation.add_relationship( - {relationship_t::kDependency, - common::to_id(*arg.getAsType() - ->getAs() - ->getAsTagDecl())}); - } - } - } - } - else if (argument_kind == clang::TemplateArgument::ArgKind::Integral) { - argument.is_template_parameter(false); - argument.set_type( - std::to_string(arg.getAsIntegral().getExtValue())); - } - else if (argument_kind == - clang::TemplateArgument::ArgKind::Expression) { - argument.is_template_parameter(false); - argument.set_type(get_source_text( - arg.getAsExpr()->getSourceRange(), source_manager_)); - } - else { - LOG_ERROR("Unsupported argument type {}", arg.getKind()); - } - - // We can figure this only when we encounter variadic param in - // the list of template params, from then this variable is true - // and we can process following template parameters as belonging - // to the variadic tuple - auto variadic_params = false; - - // In case any of the template arguments are base classes, add - // them as parents of the current template instantiation class - if (template_base_params.size() > 0) { - variadic_params = build_template_instantiation_add_base_classes( - template_instantiation, template_base_params, arg_index, - variadic_params, argument); - } - - LOG_DBG("Adding template argument {} to template " - "specialization/instantiation {}", - argument.name(), template_instantiation.name()); - - simplify_system_template( - argument, argument.to_string(config().using_namespace(), false)); - template_instantiation.add_template(std::move(argument)); - - arg_index++; - } + build_template_instantiation_process_template_arguments(parent, + template_base_params, template_type.template_arguments(), + template_instantiation, full_template_specialization_name, + template_decl); // First try to find the best match for this template in partially // specialized templates @@ -1998,6 +1587,257 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( return template_instantiation_ptr; } +void translation_unit_visitor:: + build_template_instantiation_process_template_arguments( + std::optional &parent, + std::deque> &template_base_params, + // const clang::TemplateSpecializationType &template_type, + const clang::ArrayRef &template_args, + class_ &template_instantiation, + const std::string &full_template_specialization_name, + const clang::TemplateDecl *template_decl) +{ + auto arg_index = 0U; + for (const auto &arg : template_args) { + const auto argument_kind = arg.getKind(); + template_parameter argument; + if (argument_kind == clang::TemplateArgument::Template) { + build_template_instantiation_process_template_argument( + arg, argument); + } + else if (argument_kind == clang::TemplateArgument::Type) { + build_template_instantiation_process_type_argument(parent, + full_template_specialization_name, template_decl, arg, + template_instantiation, argument); + } + else if (argument_kind == clang::TemplateArgument::Integral) { + build_template_instantiation_process_integral_argument( + arg, argument); + } + else if (argument_kind == clang::TemplateArgument::Expression) { + build_template_instantiation_process_expression_argument( + arg, argument); + } + else { + LOG_ERROR("Unsupported argument type {}", arg.getKind()); + } + + // We can figure this only when we encounter variadic param in + // the list of template params, from then this variable is true + // and we can process following template parameters as belonging + // to the variadic tuple + auto variadic_params = false; + + // In case any of the template arguments are base classes, add + // them as parents of the current template instantiation class + if (template_base_params.size() > 0) { + variadic_params = build_template_instantiation_add_base_classes( + template_instantiation, template_base_params, arg_index, + variadic_params, argument); + } + + LOG_DBG("Adding template argument {} to template " + "specialization/instantiation {}", + argument.name(), template_instantiation.name()); + + simplify_system_template( + argument, argument.to_string(config().using_namespace(), false)); + + template_instantiation.add_template(std::move(argument)); + + arg_index++; + } +} + +void translation_unit_visitor:: + build_template_instantiation_process_template_argument( + const clang::TemplateArgument &arg, template_parameter &argument) const +{ + argument.is_template_parameter(true); + auto arg_name = + arg.getAsTemplate().getAsTemplateDecl()->getQualifiedNameAsString(); + argument.set_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) +{ + assert(arg.getKind() == clang::TemplateArgument::Type); + + argument.is_template_parameter(false); + + // If this is a nested template type - add nested templates as + // template arguments + if (arg.getAsType()->getAs()) { + + for (const auto ¶m_type : + arg.getAsType()->getAs()->param_types()) { + + if (!param_type->getAs()) + continue; + + auto classTemplateSpecialization = + llvm::dyn_cast( + param_type->getAsRecordDecl()); + + if (classTemplateSpecialization) { + // Read arg info as needed. + auto nested_template_instantiation = + build_template_instantiation_from_class_template_specialization( + *classTemplateSpecialization, + *param_type->getAs(), + diagram().should_include( + full_template_specialization_name) + ? std::make_optional(&template_instantiation) + : parent); + + const auto nested_template_name = + classTemplateSpecialization->getQualifiedNameAsString(); + + auto [tinst_ns, tinst_name] = + cx::util::split_ns(nested_template_name); + + 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)); + } + } + } + } + else if (arg.getAsType()->getAs()) { + const auto *nested_template_type = + arg.getAsType()->getAs(); + + const auto nested_template_name = + nested_template_type->getTemplateName() + .getAsTemplateDecl() + ->getQualifiedNameAsString(); + + auto [tinst_ns, tinst_name] = cx::util::split_ns(nested_template_name); + + argument.set_name(nested_template_name); + + auto nested_template_instantiation = build_template_instantiation( + *arg.getAsType()->getAs(), + diagram().should_include(full_template_specialization_name) + ? std::make_optional(&template_instantiation) + : parent); + + argument.set_id(nested_template_instantiation->id()); + + 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)); + + if (nested_template_instantiation && + diagram().should_include( + nested_template_instantiation->full_name(false))) { + if (diagram().should_include(full_template_specialization_name)) { + template_instantiation.add_relationship( + {relationship_t::kDependency, + nested_template_instantiation->id()}); + } + else { + 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)); + } + } + else if (arg.getAsType()->getAs()) { + argument.is_template_parameter(true); + argument.set_name( + to_string(arg.getAsType(), template_decl->getASTContext())); + } + else { + // This is just a regular record type + build_template_instantiation_process_tag_argument( + template_instantiation, full_template_specialization_name, + template_decl, arg, argument); + } +} + +void translation_unit_visitor:: + build_template_instantiation_process_integral_argument( + const clang::TemplateArgument &arg, template_parameter &argument) const +{ + assert(arg.getKind() == clang::TemplateArgument::Integral); + + argument.is_template_parameter(false); + argument.set_type(std::to_string(arg.getAsIntegral().getExtValue())); +} + +void translation_unit_visitor:: + build_template_instantiation_process_expression_argument( + const clang::TemplateArgument &arg, template_parameter &argument) const +{ + assert(arg.getKind() == clang::TemplateArgument::Expression); + + argument.is_template_parameter(false); + argument.set_type( + get_source_text(arg.getAsExpr()->getSourceRange(), source_manager_)); +} + +void translation_unit_visitor:: + build_template_instantiation_process_tag_argument( + class_ &template_instantiation, + const std::string &full_template_specialization_name, + const clang::TemplateDecl *template_decl, + const clang::TemplateArgument &arg, template_parameter &argument) +{ + assert(arg.getKind() == clang::TemplateArgument::Type); + + argument.is_template_parameter(false); + + argument.set_name( + to_string(arg.getAsType(), template_decl->getASTContext())); + + if (arg.getAsType()->getAs() && + arg.getAsType()->getAs()->getAsRecordDecl()) { + argument.set_id(common::to_id(arg)); + + if (diagram().should_include(full_template_specialization_name)) { + // Add dependency relationship to the parent + // template + template_instantiation.add_relationship( + {relationship_t::kDependency, common::to_id(arg)}); + } + } + else if (arg.getAsType()->getAs()) { + if (arg.getAsType()->getAs()->getAsTagDecl()) { + template_instantiation.add_relationship( + {relationship_t::kDependency, common::to_id(arg)}); + } + } +} + bool translation_unit_visitor::build_template_instantiation_add_base_classes( class_ &tinst, std::deque> &template_base_params, diff --git a/src/class_diagram/visitor/translation_unit_visitor.h b/src/class_diagram/visitor/translation_unit_visitor.h index 98852bd9..8a535ff6 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.h +++ b/src/class_diagram/visitor/translation_unit_visitor.h @@ -147,6 +147,42 @@ private: int arg_index, bool variadic_params, const clanguml::class_diagram::model::template_parameter &ct) const; + void build_template_instantiation_process_template_arguments( + std::optional &parent, + std::deque> &template_base_params, + // const clang::TemplateSpecializationType &template_type, + const clang::ArrayRef &template_args, + model::class_ &template_instantiation, + const std::string &full_template_specialization_name, + const clang::TemplateDecl *template_decl); + + void build_template_instantiation_process_tag_argument( + model::class_ &template_instantiation, + const std::string &full_template_specialization_name, + const clang::TemplateDecl *template_decl, + const clang::TemplateArgument &arg, + model::template_parameter &argument); + + void build_template_instantiation_process_expression_argument( + const clang::TemplateArgument &arg, + model::template_parameter &argument) const; + + void build_template_instantiation_process_integral_argument( + const clang::TemplateArgument &arg, + model::template_parameter &argument) const; + + void 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, + model::template_parameter &argument); + + void build_template_instantiation_process_template_argument( + const clang::TemplateArgument &arg, + model::template_parameter &argument) const; + void process_function_parameter_find_relationships_in_template( clanguml::class_diagram::model::class_ &c, const std::set &template_parameter_names, diff --git a/src/common/clang_utils.cc b/src/common/clang_utils.cc index 35ad9240..81d70b03 100644 --- a/src/common/clang_utils.cc +++ b/src/common/clang_utils.cc @@ -62,4 +62,20 @@ template <> id_t to_id(const std::filesystem::path &file) return to_id(file.lexically_normal().string()); } +template <> id_t to_id(const clang::TemplateArgument &template_argument) +{ + if (template_argument.getKind() == clang::TemplateArgument::Type) { + if (template_argument.getAsType()->getAs()) + return to_id(*template_argument.getAsType() + ->getAs() + ->getAsTagDecl()); + else if (template_argument.getAsType()->getAs()) + return to_id(*template_argument.getAsType() + ->getAs() + ->getAsRecordDecl()); + } + + throw std::runtime_error("Cannot generate id for template argument"); +} + }