Improved handling of method template deductions

This commit is contained in:
Bartek Kryza
2023-04-18 23:56:09 +02:00
parent 6323ce8a92
commit 7f9d698afc
7 changed files with 197 additions and 21 deletions

View File

@@ -72,8 +72,6 @@ std::unique_ptr<class_> template_builder::build(const clang::NamedDecl *cls,
const clang::TemplateSpecializationType &template_type_decl,
std::optional<clanguml::class_diagram::model::class_ *> parent)
{
// TODO: Make sure we only build instantiation once
//
// Here we'll hold the template base class params to replace with the
// instantiated values
@@ -641,7 +639,6 @@ template_parameter template_builder::process_type_argument(
argument.set_type(unexposed_type_name);
}
else if (type_name.find("type-parameter-") == 0) {
// argument = template_parameter::make_template_type({});
auto maybe_arg = get_template_argument_from_type_parameter_string(
cls, type_name);
@@ -651,7 +648,7 @@ template_parameter template_builder::process_type_argument(
// Otherwise just set the name for the template argument to
// whatever clang says
argument.set_name(type_name);
return template_parameter::make_template_type(type_name);
}
}
@@ -732,22 +729,146 @@ bool template_builder::find_relationships_in_unexposed_template_params(
std::optional<template_parameter>
template_builder::get_template_argument_from_type_parameter_string(
const clang::Decl *decl, const std::string &return_type_name) const
const clang::Decl *decl, const std::string &type_name) const
{
if (const auto *template_decl =
llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(decl);
template_decl != nullptr &&
return_type_name.find("type-parameter-") == 0) {
template_decl != nullptr && type_name.find("type-parameter-") == 0) {
[[maybe_unused]] const auto [depth, index] =
common::extract_template_parameter_index(return_type_name);
if (type_name.rfind("type-parameter-") > 0 &&
type_name.find("::*") != std::string::npos) {
if (type_name.find("::*)") == std::string::npos) {
// This is a method invocation template, e.g.
// template <typename M, typename C>
// struct invocable<type-parameter-0-0 type-parameter-0-1::*>
// {};
const auto [depth0, index0, qualifier0] =
common::extract_template_parameter_index(
type_name.substr(0, type_name.find(' ')));
std::string param_name = return_type_name;
const auto [depth1, index1, qualifier1] =
common::extract_template_parameter_index(type_name.substr(
type_name.find(' ') + 1, type_name.find(':')));
auto template_param =
template_parameter::make_template_type({});
template_param.set_method_template(true);
std::string param_name = type_name;
for (auto i = 0U;
i < template_decl->getDescribedTemplateParams()->size();
i++) {
const auto *param =
template_decl->getDescribedTemplateParams()->getParam(
i);
if (i == index0) {
param_name = param->getNameAsString();
template_param.add_template_param(
template_parameter::make_template_type(param_name));
}
if (i == index1) {
param_name = param->getNameAsString();
template_param.add_template_param(
template_parameter::make_template_type(param_name));
}
}
template_param.set_method_qualifier(
type_name.substr(type_name.find("::*") + 3));
return template_param;
}
else {
// Here we're dealing with method type with args, e.g.:
// type-parameter-0-0
// (type-parameter-0-1::*)(type-parameter-0-2)
const auto [depth0, index0, qualifier0] =
common::extract_template_parameter_index(
type_name.substr(0, type_name.find(' ')));
const auto [depth1, index1, qualifier1] =
common::extract_template_parameter_index(type_name.substr(
type_name.find(" (") + 2, type_name.find(':')));
auto template_param =
template_parameter::make_template_type({});
template_param.set_method_template(true);
// TODO: Handle args
for (auto i = 0U;
i < template_decl->getDescribedTemplateParams()->size();
i++) {
const auto *param =
template_decl->getDescribedTemplateParams()->getParam(
i);
if (i == index0) {
template_param.add_template_param(
template_parameter::make_template_type(
param->getNameAsString()));
}
if (i == index1) {
template_param.add_template_param(
template_parameter::make_template_type(
param->getNameAsString()));
}
}
return template_param;
}
}
else {
const auto [depth, index, qualifier] =
common::extract_template_parameter_index(type_name);
std::string param_name = type_name;
clang::ClassTemplateSpecializationDecl *template_decl_at_depth =
const_cast<clang::ClassTemplateSpecializationDecl *>(
template_decl);
for (auto i = 0U; i <
template_decl_at_depth->getDescribedTemplateParams()->size();
i++) {
const auto *param =
template_decl_at_depth->getDescribedTemplateParams()
->getParam(i);
if (i == index) {
param_name = param->getNameAsString();
auto template_param =
template_parameter::make_template_type(param_name);
template_param.is_variadic(param->isParameterPack());
return template_param;
}
}
}
}
else if (const auto *alias_decl =
llvm::dyn_cast<clang::TypeAliasTemplateDecl>(decl);
alias_decl != nullptr && type_name.find("type-parameter-") == 0) {
const auto [depth, index, qualifier] =
common::extract_template_parameter_index(type_name);
std::string param_name = type_name;
const auto *template_decl_at_depth = alias_decl;
template_decl_at_depth->dump();
for (auto i = 0U;
i < template_decl->getDescribedTemplateParams()->size(); i++) {
i < template_decl_at_depth->getTemplateParameters()->size(); i++) {
const auto *param =
template_decl->getDescribedTemplateParams()->getParam(i);
template_decl_at_depth->getTemplateParameters()->getParam(i);
if (i == index) {
param_name = param->getNameAsString();

View File

@@ -122,7 +122,7 @@ public:
std::optional<template_parameter>
get_template_argument_from_type_parameter_string(
const clang::Decl *decl, const std::string &return_type_name) const;
const clang::Decl *decl, const std::string &type_name) const;
common::visitor::ast_id_mapper &id_mapper();

View File

@@ -269,17 +269,24 @@ std::string get_source_text(
return get_source_text_raw(printable_range, sm);
}
std::pair<unsigned int, unsigned int> extract_template_parameter_index(
const std::string &type_parameter)
std::tuple<unsigned int, unsigned int, std::string>
extract_template_parameter_index(const std::string &type_parameter)
{
assert(type_parameter.find("type-parameter-") == 0);
auto toks =
util::split(type_parameter.substr(strlen("type-parameter-")), "-");
auto type_parameter_and_suffix = util::split(type_parameter, " ");
assert(toks.size() == 2);
auto toks = util::split(
type_parameter_and_suffix.front().substr(strlen("type-parameter-")),
"-");
return {std::stoi(toks.at(0)), std::stoi(toks.at(1))};
std::string qualifier;
if (type_parameter_and_suffix.size() > 1) {
qualifier = type_parameter_and_suffix.at(1);
}
return {std::stoi(toks.at(0)), std::stoi(toks.at(1)), std::move(qualifier)};
}
bool is_subexpr_of(const clang::Stmt *parent_stmt, const clang::Stmt *sub_stmt)

View File

@@ -98,8 +98,8 @@ std::string get_source_text_raw(
std::string get_source_text(
clang::SourceRange range, const clang::SourceManager &sm);
std::pair<unsigned int, unsigned int> extract_template_parameter_index(
const std::string &type_parameter);
std::tuple<unsigned int, unsigned int, std::string>
extract_template_parameter_index(const std::string &type_parameter);
/**
* @brief Check if an expression is contained in another expression

View File

@@ -215,6 +215,13 @@ std::string template_parameter::to_string(
"{}({})", return_type, fmt::join(function_args, ","));
}
if (is_method_template()) {
assert(template_params().size() == 2);
return fmt::format("{} {}::*{}", template_params().at(0).name().value(),
template_params().at(1).name().value(), method_qualifier());
}
std::string res;
const auto maybe_type = type();
if (maybe_type) {

View File

@@ -182,6 +182,14 @@ public:
bool is_function_template() const { return is_function_template_; }
void set_method_template(bool mt) { is_method_template_ = mt; }
bool is_method_template() const { return is_method_template_; }
void set_method_qualifier(const std::string &q) { method_qualifier_ = q; }
const std::string &method_qualifier() const { return method_qualifier_; }
private:
template_parameter() = default;
@@ -210,6 +218,10 @@ private:
bool is_function_template_{false};
bool is_method_template_{false};
std::string method_qualifier_;
/// Stores optional fully qualified name of constraint for this template
/// parameter
std::optional<std::string> concept_constraint_;

View File

@@ -94,6 +94,35 @@ TEST_CASE("Test replace_all", "[unit-test]")
CHECK(text == orig);
}
TEST_CASE("Test extract_template_parameter_index", "[unit-test]")
{
using namespace clanguml::common;
{
const auto [depth, index, qualifier] =
extract_template_parameter_index("type-parameter-0-0");
CHECK(depth == 0);
CHECK(index == 0);
CHECK(qualifier.empty());
}
{
const auto [depth, index, qualifier] =
extract_template_parameter_index("type-parameter-0-0 &&");
CHECK(depth == 0);
CHECK(index == 0);
CHECK(qualifier == "&&");
}
{
const auto [depth, index, qualifier] =
extract_template_parameter_index("type-parameter-12-678 const&");
CHECK(depth == 12);
CHECK(index == 678);
CHECK(qualifier == "const&");
}
}
TEST_CASE("Test parse_unexposed_template_params", "[unit-test]")
{
using namespace clanguml::common;