Fixed handling of template template specialization parameters in instantiation deduction
This commit is contained in:
@@ -19,6 +19,7 @@
|
|||||||
#include "template_builder.h"
|
#include "template_builder.h"
|
||||||
#include "common/clang_utils.h"
|
#include "common/clang_utils.h"
|
||||||
#include "translation_unit_visitor.h"
|
#include "translation_unit_visitor.h"
|
||||||
|
#include <clang/Lex/Lexer.h>
|
||||||
|
|
||||||
namespace clanguml::class_diagram::visitor {
|
namespace clanguml::class_diagram::visitor {
|
||||||
|
|
||||||
@@ -289,7 +290,7 @@ template_builder::build_from_class_template_specialization(
|
|||||||
auto &template_instantiation = *template_instantiation_ptr;
|
auto &template_instantiation = *template_instantiation_ptr;
|
||||||
template_instantiation.is_struct(template_specialization.isStruct());
|
template_instantiation.is_struct(template_specialization.isStruct());
|
||||||
|
|
||||||
const auto *template_decl =
|
const clang::ClassTemplateDecl *template_decl =
|
||||||
template_specialization.getSpecializedTemplate();
|
template_specialization.getSpecializedTemplate();
|
||||||
|
|
||||||
auto qualified_name = template_decl->getQualifiedNameAsString();
|
auto qualified_name = template_decl->getQualifiedNameAsString();
|
||||||
@@ -364,27 +365,36 @@ void template_builder::process_template_arguments(
|
|||||||
const clang::ArrayRef<clang::TemplateArgument> &template_args,
|
const clang::ArrayRef<clang::TemplateArgument> &template_args,
|
||||||
class_ &template_instantiation, const clang::TemplateDecl *template_decl)
|
class_ &template_instantiation, const clang::TemplateDecl *template_decl)
|
||||||
{
|
{
|
||||||
auto arg_index = 0;
|
auto arg_index{0};
|
||||||
|
|
||||||
for (const auto &arg : template_args) {
|
for (const auto &arg : template_args) {
|
||||||
// Argument can be a parameter pack in which case it gives multiple
|
// Argument can be a parameter pack in which case it gives multiple
|
||||||
// arguments
|
// arguments
|
||||||
std::vector<template_parameter> 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
|
// templates 'nicer' - i.e. skipping the allocators and comparators
|
||||||
// TODO: Change this to ignore only when the arguments are set to
|
// TODO: Change this to ignore only when the arguments are set to
|
||||||
// default values, and add them when they are specifically
|
// default values, and add them when they are specifically
|
||||||
// overriden
|
// overridden
|
||||||
const auto *maybe_type_parm_decl =
|
if (!diagram().should_include(
|
||||||
clang::dyn_cast<clang::TemplateTypeParmDecl>(
|
template_decl->getQualifiedNameAsString())) {
|
||||||
template_decl->getTemplateParameters()->getParam(
|
const auto *maybe_type_parm_decl =
|
||||||
std::min<int>(arg_index,
|
clang::dyn_cast<clang::TemplateTypeParmDecl>(
|
||||||
template_decl->getTemplateParameters()->size() - 1)));
|
template_decl->getTemplateParameters()->getParam(
|
||||||
if (maybe_type_parm_decl &&
|
std::min<int>(arg_index,
|
||||||
maybe_type_parm_decl->hasDefaultArgument()) {
|
template_decl->getTemplateParameters()->size() -
|
||||||
continue;
|
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,
|
argument_process_dispatch(parent, cls, template_instantiation,
|
||||||
template_decl, arg, arg_index, arguments);
|
template_decl, arg, arg_index, arguments);
|
||||||
|
|
||||||
@@ -430,10 +440,16 @@ void template_builder::argument_process_dispatch(
|
|||||||
const clang::TemplateArgument &arg, size_t argument_index,
|
const clang::TemplateArgument &arg, size_t argument_index,
|
||||||
std::vector<template_parameter> &argument)
|
std::vector<template_parameter> &argument)
|
||||||
{
|
{
|
||||||
|
LOG_DBG("Processing argument {} in template class: {}", argument_index,
|
||||||
|
cls->getQualifiedNameAsString());
|
||||||
|
|
||||||
switch (arg.getKind()) {
|
switch (arg.getKind()) {
|
||||||
case clang::TemplateArgument::Null:
|
case clang::TemplateArgument::Null:
|
||||||
argument.push_back(process_null_argument(arg));
|
argument.push_back(process_null_argument(arg));
|
||||||
break;
|
break;
|
||||||
|
case clang::TemplateArgument::Template:
|
||||||
|
argument.push_back(process_template_argument(arg));
|
||||||
|
break;
|
||||||
case clang::TemplateArgument::Type:
|
case clang::TemplateArgument::Type:
|
||||||
argument.push_back(process_type_argument(parent, cls, template_decl,
|
argument.push_back(process_type_argument(parent, cls, template_decl,
|
||||||
arg.getAsType(), template_instantiation, argument_index));
|
arg.getAsType(), template_instantiation, argument_index));
|
||||||
@@ -446,9 +462,6 @@ void template_builder::argument_process_dispatch(
|
|||||||
case clang::TemplateArgument::Integral:
|
case clang::TemplateArgument::Integral:
|
||||||
argument.push_back(process_integral_argument(arg));
|
argument.push_back(process_integral_argument(arg));
|
||||||
break;
|
break;
|
||||||
case clang::TemplateArgument::Template:
|
|
||||||
argument.push_back(process_template_argument(arg));
|
|
||||||
break;
|
|
||||||
case clang::TemplateArgument::TemplateExpansion:
|
case clang::TemplateArgument::TemplateExpansion:
|
||||||
argument.push_back(process_template_expansion(arg));
|
argument.push_back(process_template_expansion(arg));
|
||||||
break;
|
break;
|
||||||
@@ -576,8 +589,8 @@ template_parameter template_builder::process_type_argument(
|
|||||||
|
|
||||||
std::optional<template_parameter> argument;
|
std::optional<template_parameter> argument;
|
||||||
|
|
||||||
LOG_DBG("Processing template {} type argument: {}, {}, {}",
|
LOG_DBG("Processing template {} type argument {}: {}, {}, {}",
|
||||||
template_decl->getQualifiedNameAsString(), type_name,
|
template_decl->getQualifiedNameAsString(), argument_index, type_name,
|
||||||
type->getTypeClassName(),
|
type->getTypeClassName(),
|
||||||
common::to_string(type, cls->getASTContext()));
|
common::to_string(type, cls->getASTContext()));
|
||||||
|
|
||||||
@@ -596,12 +609,12 @@ template_parameter template_builder::process_type_argument(
|
|||||||
if (argument)
|
if (argument)
|
||||||
return *argument;
|
return *argument;
|
||||||
|
|
||||||
argument = try_as_template_specialization_type(parent, cls, template_decl,
|
argument = try_as_template_parm_type(cls, template_decl, type);
|
||||||
type, template_instantiation, argument_index);
|
|
||||||
if (argument)
|
if (argument)
|
||||||
return *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)
|
if (argument)
|
||||||
return *argument;
|
return *argument;
|
||||||
|
|
||||||
@@ -619,6 +632,10 @@ template_parameter template_builder::process_type_argument(
|
|||||||
if (argument)
|
if (argument)
|
||||||
return *argument;
|
return *argument;
|
||||||
|
|
||||||
|
argument = try_as_builtin_type(parent, type, template_decl);
|
||||||
|
if (argument)
|
||||||
|
return *argument;
|
||||||
|
|
||||||
// fallback
|
// fallback
|
||||||
return template_parameter::make_argument(type_name);
|
return template_parameter::make_argument(type_name);
|
||||||
}
|
}
|
||||||
@@ -952,9 +969,28 @@ template_builder::try_as_template_specialization_type(
|
|||||||
auto argument = template_parameter::make_argument("");
|
auto argument = template_parameter::make_argument("");
|
||||||
type = consume_context(type, argument);
|
type = consume_context(type, argument);
|
||||||
|
|
||||||
const auto nested_type_name = nested_template_type->getTemplateName()
|
auto nested_type_name = nested_template_type->getTemplateName()
|
||||||
.getAsTemplateDecl()
|
.getAsTemplateDecl()
|
||||||
->getQualifiedNameAsString();
|
->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);
|
argument.set_type(nested_type_name);
|
||||||
|
|
||||||
@@ -1008,6 +1044,8 @@ std::optional<template_parameter> template_builder::try_as_template_parm_type(
|
|||||||
auto type_parameter =
|
auto type_parameter =
|
||||||
common::dereference(type)->getAs<clang::TemplateTypeParmType>();
|
common::dereference(type)->getAs<clang::TemplateTypeParmType>();
|
||||||
|
|
||||||
|
auto type_name = common::to_string(type, &cls->getASTContext());
|
||||||
|
|
||||||
if (type_parameter == nullptr) {
|
if (type_parameter == nullptr) {
|
||||||
if (common::dereference(type)->getAs<clang::PackExpansionType>()) {
|
if (common::dereference(type)->getAs<clang::PackExpansionType>()) {
|
||||||
is_variadic = true;
|
is_variadic = true;
|
||||||
@@ -1026,8 +1064,6 @@ std::optional<template_parameter> template_builder::try_as_template_parm_type(
|
|||||||
|
|
||||||
argument.is_variadic(is_variadic);
|
argument.is_variadic(is_variadic);
|
||||||
|
|
||||||
auto type_name = common::to_string(type, &cls->getASTContext());
|
|
||||||
|
|
||||||
auto type_parameter_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);
|
ensure_lambda_type_is_relative(type_parameter_name);
|
||||||
@@ -1141,6 +1177,23 @@ std::optional<template_parameter> template_builder::try_as_enum_type(
|
|||||||
return argument;
|
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,
|
bool template_builder::add_base_classes(class_ &tinst,
|
||||||
std::deque<std::tuple<std::string, int, bool>> &template_base_params,
|
std::deque<std::tuple<std::string, int, bool>> &template_base_params,
|
||||||
int arg_index, bool variadic_params, const template_parameter &ct)
|
int arg_index, bool variadic_params, const template_parameter &ct)
|
||||||
|
|||||||
@@ -149,6 +149,10 @@ public:
|
|||||||
const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
|
const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
|
||||||
clang::QualType &type, class_ &template_instantiation);
|
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<template_parameter> try_as_member_pointer(
|
||||||
std::optional<clanguml::class_diagram::model::class_ *> &parent,
|
std::optional<clanguml::class_diagram::model::class_ *> &parent,
|
||||||
const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
|
const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
|
||||||
|
|||||||
@@ -59,17 +59,19 @@ TEST_CASE("t00051", "[test-case][class]")
|
|||||||
REQUIRE_THAT(puml, (IsMethod<Public>("ff", "void")));
|
REQUIRE_THAT(puml, (IsMethod<Public>("ff", "void")));
|
||||||
|
|
||||||
REQUIRE_THAT(puml,
|
REQUIRE_THAT(puml,
|
||||||
IsClassTemplate(
|
IsClassTemplate("B",
|
||||||
"B", "(lambda at ../../tests/t00051/t00051.cc:43:18)"));
|
"(lambda at ../../tests/t00051/t00051.cc:43:18),(lambda at "
|
||||||
//,(lambda at ../../tests/t00051/t00051.cc:43:27)"));
|
"../../tests/t00051/t00051.cc:43:27)"));
|
||||||
|
|
||||||
REQUIRE_THAT(puml,
|
REQUIRE_THAT(puml,
|
||||||
IsInstantiation(_A("B<F,FF=F>"),
|
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,
|
REQUIRE_THAT(puml,
|
||||||
IsDependency(_A("A"),
|
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(
|
save_puml(
|
||||||
config.output_directory() + "/" + diagram->name + ".puml", puml);
|
config.output_directory() + "/" + diagram->name + ".puml", puml);
|
||||||
|
|||||||
@@ -86,14 +86,16 @@ template <typename U> struct A<U(...)> {
|
|||||||
bool u;
|
bool u;
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
template <template <typename> class C, typename T> struct A<C<T>> {
|
||||||
// template <typename T> struct eval;
|
C<T> c;
|
||||||
//
|
};
|
||||||
// template <template <typename, typename...> class TT, typename T1,
|
|
||||||
// typename... Rest>
|
template <template <typename, typename...> class C, typename T,
|
||||||
// struct eval<TT<T1, Rest...>> { };
|
typename... Args>
|
||||||
//
|
struct A<C<T, Args...>> {
|
||||||
// eval<A<int>> eA;
|
C<T> c;
|
||||||
// eval<std::map<int, float>> eB;
|
std::tuple<Args...> args;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,6 +52,8 @@ TEST_CASE("t00062", "[test-case][class]")
|
|||||||
REQUIRE_THAT(puml, IsClassTemplate("A", "char[1000]"));
|
REQUIRE_THAT(puml, IsClassTemplate("A", "char[1000]"));
|
||||||
|
|
||||||
REQUIRE_THAT(puml, IsClassTemplate("A", "U(...)"));
|
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 &>")));
|
||||||
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]>")));
|
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<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(
|
save_puml(
|
||||||
config.output_directory() + "/" + diagram->name + ".puml", puml);
|
config.output_directory() + "/" + diagram->name + ".puml", puml);
|
||||||
|
|||||||
Reference in New Issue
Block a user