diff --git a/src/class_diagram/visitor/template_builder.cc b/src/class_diagram/visitor/template_builder.cc index dd30ff48..56150be0 100644 --- a/src/class_diagram/visitor/template_builder.cc +++ b/src/class_diagram/visitor/template_builder.cc @@ -19,6 +19,7 @@ #include "template_builder.h" #include "common/clang_utils.h" #include "translation_unit_visitor.h" +#include namespace clanguml::class_diagram::visitor { @@ -289,7 +290,7 @@ template_builder::build_from_class_template_specialization( auto &template_instantiation = *template_instantiation_ptr; template_instantiation.is_struct(template_specialization.isStruct()); - const auto *template_decl = + const clang::ClassTemplateDecl *template_decl = template_specialization.getSpecializedTemplate(); auto qualified_name = template_decl->getQualifiedNameAsString(); @@ -364,27 +365,36 @@ void template_builder::process_template_arguments( const clang::ArrayRef &template_args, class_ &template_instantiation, const clang::TemplateDecl *template_decl) { - auto arg_index = 0; + auto arg_index{0}; + for (const auto &arg : template_args) { // Argument can be a parameter pack in which case it gives multiple // arguments std::vector arguments; - // For now ignore the default template arguments to make the system + // For now ignore the default template arguments of templates which + // do not match the inclusion filters, to make the system // templates 'nicer' - i.e. skipping the allocators and comparators // TODO: Change this to ignore only when the arguments are set to // default values, and add them when they are specifically - // overriden - const auto *maybe_type_parm_decl = - clang::dyn_cast( - template_decl->getTemplateParameters()->getParam( - std::min(arg_index, - template_decl->getTemplateParameters()->size() - 1))); - if (maybe_type_parm_decl && - maybe_type_parm_decl->hasDefaultArgument()) { - continue; + // overridden + if (!diagram().should_include( + template_decl->getQualifiedNameAsString())) { + const auto *maybe_type_parm_decl = + clang::dyn_cast( + template_decl->getTemplateParameters()->getParam( + std::min(arg_index, + template_decl->getTemplateParameters()->size() - + 1))); + if (maybe_type_parm_decl && + maybe_type_parm_decl->hasDefaultArgument()) { + continue; + } } + // + // Handle the template parameter/argument based on its kind + // argument_process_dispatch(parent, cls, template_instantiation, template_decl, arg, arg_index, arguments); @@ -430,10 +440,16 @@ void template_builder::argument_process_dispatch( const clang::TemplateArgument &arg, size_t argument_index, std::vector &argument) { + LOG_DBG("Processing argument {} in template class: {}", argument_index, + cls->getQualifiedNameAsString()); + switch (arg.getKind()) { case clang::TemplateArgument::Null: argument.push_back(process_null_argument(arg)); break; + case clang::TemplateArgument::Template: + argument.push_back(process_template_argument(arg)); + break; case clang::TemplateArgument::Type: argument.push_back(process_type_argument(parent, cls, template_decl, arg.getAsType(), template_instantiation, argument_index)); @@ -446,9 +462,6 @@ void template_builder::argument_process_dispatch( case clang::TemplateArgument::Integral: argument.push_back(process_integral_argument(arg)); break; - case clang::TemplateArgument::Template: - argument.push_back(process_template_argument(arg)); - break; case clang::TemplateArgument::TemplateExpansion: argument.push_back(process_template_expansion(arg)); break; @@ -576,8 +589,8 @@ template_parameter template_builder::process_type_argument( std::optional argument; - LOG_DBG("Processing template {} type argument: {}, {}, {}", - template_decl->getQualifiedNameAsString(), type_name, + LOG_DBG("Processing template {} type argument {}: {}, {}, {}", + template_decl->getQualifiedNameAsString(), argument_index, type_name, type->getTypeClassName(), common::to_string(type, cls->getASTContext())); @@ -596,12 +609,12 @@ template_parameter template_builder::process_type_argument( if (argument) return *argument; - argument = try_as_template_specialization_type(parent, cls, template_decl, - type, template_instantiation, argument_index); + argument = try_as_template_parm_type(cls, template_decl, type); if (argument) return *argument; - argument = try_as_template_parm_type(cls, template_decl, type); + argument = try_as_template_specialization_type(parent, cls, template_decl, + type, template_instantiation, argument_index); if (argument) return *argument; @@ -619,6 +632,10 @@ template_parameter template_builder::process_type_argument( if (argument) return *argument; + argument = try_as_builtin_type(parent, type, template_decl); + if (argument) + return *argument; + // fallback return template_parameter::make_argument(type_name); } @@ -952,9 +969,28 @@ template_builder::try_as_template_specialization_type( auto argument = template_parameter::make_argument(""); type = consume_context(type, argument); - const auto nested_type_name = nested_template_type->getTemplateName() - .getAsTemplateDecl() - ->getQualifiedNameAsString(); + auto nested_type_name = nested_template_type->getTemplateName() + .getAsTemplateDecl() + ->getQualifiedNameAsString(); + + if (clang::dyn_cast( + nested_template_type->getTemplateName().getAsTemplateDecl()) != + nullptr) { + if (const auto *template_specialization_decl = + clang::dyn_cast(cls); + template_specialization_decl != nullptr) { + nested_type_name = + template_specialization_decl->getDescribedTemplateParams() + ->getParam(argument_index) + ->getNameAsString(); + } + else { + // fallback + nested_type_name = "template"; + } + + argument.is_template_template_parameter(true); + } argument.set_type(nested_type_name); @@ -1008,6 +1044,8 @@ std::optional template_builder::try_as_template_parm_type( auto type_parameter = common::dereference(type)->getAs(); + auto type_name = common::to_string(type, &cls->getASTContext()); + if (type_parameter == nullptr) { if (common::dereference(type)->getAs()) { is_variadic = true; @@ -1026,8 +1064,6 @@ std::optional template_builder::try_as_template_parm_type( argument.is_variadic(is_variadic); - auto type_name = common::to_string(type, &cls->getASTContext()); - auto type_parameter_name = common::to_string(type, cls->getASTContext()); ensure_lambda_type_is_relative(type_parameter_name); @@ -1141,6 +1177,23 @@ std::optional template_builder::try_as_enum_type( return argument; } +std::optional template_builder::try_as_builtin_type( + std::optional &parent, + clang::QualType &type, const clang::TemplateDecl *template_decl) +{ + const auto *builtin_type = type->getAs(); + if (builtin_type == nullptr) + return {}; + + auto type_name = common::to_string(type, template_decl->getASTContext()); + auto argument = template_parameter::make_argument(type_name); + + type = consume_context(type, argument); + argument.set_type(type_name); + + return argument; +} + bool template_builder::add_base_classes(class_ &tinst, std::deque> &template_base_params, int arg_index, bool variadic_params, const template_parameter &ct) diff --git a/src/class_diagram/visitor/template_builder.h b/src/class_diagram/visitor/template_builder.h index f3ec2985..1eaee5e8 100644 --- a/src/class_diagram/visitor/template_builder.h +++ b/src/class_diagram/visitor/template_builder.h @@ -149,6 +149,10 @@ public: const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, clang::QualType &type, class_ &template_instantiation); + std::optional try_as_builtin_type( + std::optional &parent, + clang::QualType &type, const clang::TemplateDecl *template_decl); + std::optional try_as_member_pointer( std::optional &parent, const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl, diff --git a/tests/t00051/test_case.h b/tests/t00051/test_case.h index 14269b36..6471d7cd 100644 --- a/tests/t00051/test_case.h +++ b/tests/t00051/test_case.h @@ -59,17 +59,19 @@ TEST_CASE("t00051", "[test-case][class]") REQUIRE_THAT(puml, (IsMethod("ff", "void"))); REQUIRE_THAT(puml, - IsClassTemplate( - "B", "(lambda at ../../tests/t00051/t00051.cc:43:18)")); - //,(lambda at ../../tests/t00051/t00051.cc:43:27)")); + IsClassTemplate("B", + "(lambda at ../../tests/t00051/t00051.cc:43:18),(lambda at " + "../../tests/t00051/t00051.cc:43:27)")); REQUIRE_THAT(puml, IsInstantiation(_A("B"), - _A("B<(lambda at ../../tests/t00051/t00051.cc:43:18)>"))); + _A("B<(lambda at ../../tests/t00051/t00051.cc:43:18),(lambda " + "at ../../tests/t00051/t00051.cc:43:27)>"))); REQUIRE_THAT(puml, IsDependency(_A("A"), - _A("B<(lambda at ../../tests/t00051/t00051.cc:43:18)>"))); + _A("B<(lambda at ../../tests/t00051/t00051.cc:43:18),(lambda " + "at ../../tests/t00051/t00051.cc:43:27)>"))); save_puml( config.output_directory() + "/" + diagram->name + ".puml", puml); diff --git a/tests/t00062/t00062.cc b/tests/t00062/t00062.cc index 35928618..3705c38c 100644 --- a/tests/t00062/t00062.cc +++ b/tests/t00062/t00062.cc @@ -86,14 +86,16 @@ template struct A { bool u; }; -// -// template struct eval; -// -// template