Fixed handling of template template specialization parameters in instantiation deduction

This commit is contained in:
Bartek Kryza
2023-05-03 12:41:12 +02:00
parent f01ddc63f2
commit 3fea01d452
5 changed files with 105 additions and 39 deletions

View File

@@ -19,6 +19,7 @@
#include "template_builder.h"
#include "common/clang_utils.h"
#include "translation_unit_visitor.h"
#include <clang/Lex/Lexer.h>
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<clang::TemplateArgument> &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<template_parameter> 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<clang::TemplateTypeParmDecl>(
template_decl->getTemplateParameters()->getParam(
std::min<int>(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<clang::TemplateTypeParmDecl>(
template_decl->getTemplateParameters()->getParam(
std::min<int>(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<template_parameter> &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<template_parameter> 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<clang::TemplateTemplateParmDecl>(
nested_template_type->getTemplateName().getAsTemplateDecl()) !=
nullptr) {
if (const auto *template_specialization_decl =
clang::dyn_cast<clang::ClassTemplateSpecializationDecl>(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_parameter> template_builder::try_as_template_parm_type(
auto type_parameter =
common::dereference(type)->getAs<clang::TemplateTypeParmType>();
auto type_name = common::to_string(type, &cls->getASTContext());
if (type_parameter == nullptr) {
if (common::dereference(type)->getAs<clang::PackExpansionType>()) {
is_variadic = true;
@@ -1026,8 +1064,6 @@ std::optional<template_parameter> 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_parameter> template_builder::try_as_enum_type(
return argument;
}
std::optional<template_parameter> template_builder::try_as_builtin_type(
std::optional<clanguml::class_diagram::model::class_ *> &parent,
clang::QualType &type, const clang::TemplateDecl *template_decl)
{
const auto *builtin_type = type->getAs<clang::BuiltinType>();
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<std::tuple<std::string, int, bool>> &template_base_params,
int arg_index, bool variadic_params, const template_parameter &ct)

View File

@@ -149,6 +149,10 @@ public:
const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
clang::QualType &type, class_ &template_instantiation);
std::optional<template_parameter> try_as_builtin_type(
std::optional<clanguml::class_diagram::model::class_ *> &parent,
clang::QualType &type, const clang::TemplateDecl *template_decl);
std::optional<template_parameter> try_as_member_pointer(
std::optional<clanguml::class_diagram::model::class_ *> &parent,
const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,

View File

@@ -59,17 +59,19 @@ TEST_CASE("t00051", "[test-case][class]")
REQUIRE_THAT(puml, (IsMethod<Public>("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<F,FF=F>"),
_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);

View File

@@ -86,14 +86,16 @@ template <typename U> struct A<U(...)> {
bool u;
};
//
// template <typename T> struct eval;
//
// template <template <typename, typename...> class TT, typename T1,
// typename... Rest>
// struct eval<TT<T1, Rest...>> { };
//
// eval<A<int>> eA;
// eval<std::map<int, float>> eB;
template <template <typename> class C, typename T> struct A<C<T>> {
C<T> c;
};
template <template <typename, typename...> class C, typename T,
typename... Args>
struct A<C<T, Args...>> {
C<T> c;
std::tuple<Args...> args;
};
}
}

View File

@@ -52,6 +52,8 @@ TEST_CASE("t00062", "[test-case][class]")
REQUIRE_THAT(puml, IsClassTemplate("A", "char[1000]"));
REQUIRE_THAT(puml, IsClassTemplate("A", "U(...)"));
REQUIRE_THAT(puml, IsClassTemplate("A", "C<T>"));
REQUIRE_THAT(puml, IsClassTemplate("A", "C<T,Args...>"));
REQUIRE_THAT(puml, IsInstantiation(_A("A<T>"), _A("A<U &>")));
REQUIRE_THAT(puml, IsInstantiation(_A("A<T>"), _A("A<U &&>")));
@@ -67,6 +69,9 @@ TEST_CASE("t00062", "[test-case][class]")
puml, IsInstantiation(_A("A<char[N]>"), _A("A<char[1000]>")));
REQUIRE_THAT(puml, IsInstantiation(_A("A<T>"), _A("A<U(...)>")));
REQUIRE_THAT(puml, IsInstantiation(_A("A<T>"), _A("A<U(...)>")));
REQUIRE_THAT(puml, IsInstantiation(_A("A<T>"), _A("A<C<T>>")));
REQUIRE_THAT(puml, IsInstantiation(_A("A<T>"), _A("A<C<T,Args...>>")));
save_puml(
config.output_directory() + "/" + diagram->name + ".puml", puml);