Improved unexposed template parameter tokenization
This commit is contained in:
@@ -72,6 +72,16 @@ public:
|
|||||||
|
|
||||||
int calculate_template_specialization_match(const class_ &other) const;
|
int calculate_template_specialization_match(const class_ &other) const;
|
||||||
|
|
||||||
|
void template_specialization_found(bool found)
|
||||||
|
{
|
||||||
|
template_specialization_found_ = found;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool template_specialization_found() const
|
||||||
|
{
|
||||||
|
return template_specialization_found_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool is_struct_{false};
|
bool is_struct_{false};
|
||||||
bool is_template_{false};
|
bool is_template_{false};
|
||||||
@@ -82,6 +92,8 @@ private:
|
|||||||
std::string base_template_full_name_;
|
std::string base_template_full_name_;
|
||||||
|
|
||||||
std::string full_name_;
|
std::string full_name_;
|
||||||
|
|
||||||
|
bool template_specialization_found_{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace clanguml::class_diagram::model
|
} // namespace clanguml::class_diagram::model
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "template_builder.h"
|
#include "template_builder.h"
|
||||||
|
#include "common/clang_utils.h"
|
||||||
#include "translation_unit_visitor.h"
|
#include "translation_unit_visitor.h"
|
||||||
|
|
||||||
namespace clanguml::class_diagram::visitor {
|
namespace clanguml::class_diagram::visitor {
|
||||||
@@ -241,12 +242,18 @@ std::unique_ptr<class_> template_builder::build(const clang::NamedDecl *cls,
|
|||||||
destination = best_match_full_name;
|
destination = best_match_full_name;
|
||||||
template_instantiation.add_relationship(
|
template_instantiation.add_relationship(
|
||||||
{relationship_t::kInstantiation, best_match_id});
|
{relationship_t::kInstantiation, best_match_id});
|
||||||
|
template_instantiation.template_specialization_found(true);
|
||||||
}
|
}
|
||||||
// If we can't find optimal match for parent template specialization,
|
// If we can't find optimal match for parent template specialization,
|
||||||
// just use whatever clang suggests
|
// just use whatever clang suggests
|
||||||
else if (diagram().has_element(templated_decl_local_id)) {
|
else if (diagram().has_element(templated_decl_local_id)) {
|
||||||
template_instantiation.add_relationship(
|
template_instantiation.add_relationship(
|
||||||
{relationship_t::kInstantiation, templated_decl_local_id});
|
{relationship_t::kInstantiation, templated_decl_local_id});
|
||||||
|
template_instantiation.template_specialization_found(true);
|
||||||
|
}
|
||||||
|
else if (diagram().should_include(full_template_specialization_name)) {
|
||||||
|
LOG_DBG("Skipping instantiation relationship from {} to {}",
|
||||||
|
template_instantiation_ptr->full_name(false), templated_decl_id);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG_DBG("== Cannot determine global id for specialization template {} "
|
LOG_DBG("== Cannot determine global id for specialization template {} "
|
||||||
@@ -333,12 +340,14 @@ template_builder::build_from_class_template_specialization(
|
|||||||
destination = best_match_full_name;
|
destination = best_match_full_name;
|
||||||
template_instantiation.add_relationship(
|
template_instantiation.add_relationship(
|
||||||
{relationship_t::kInstantiation, best_match_id});
|
{relationship_t::kInstantiation, best_match_id});
|
||||||
|
template_instantiation.template_specialization_found(true);
|
||||||
}
|
}
|
||||||
|
else if (diagram().has_element(templated_decl_local_id)) {
|
||||||
// If we can't find optimal match for parent template specialization,
|
// If we can't find optimal match for parent template specialization,
|
||||||
// just use whatever clang suggests
|
// just use whatever clang suggests
|
||||||
else if (diagram().has_element(templated_decl_local_id)) {
|
|
||||||
template_instantiation.add_relationship(
|
template_instantiation.add_relationship(
|
||||||
{relationship_t::kInstantiation, templated_decl_local_id});
|
{relationship_t::kInstantiation, templated_decl_local_id});
|
||||||
|
template_instantiation.template_specialization_found(true);
|
||||||
}
|
}
|
||||||
else if (diagram().should_include(qualified_name)) {
|
else if (diagram().should_include(qualified_name)) {
|
||||||
LOG_DBG("Skipping instantiation relationship from {} to {}",
|
LOG_DBG("Skipping instantiation relationship from {} to {}",
|
||||||
@@ -727,165 +736,192 @@ bool template_builder::find_relationships_in_unexposed_template_params(
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<template_parameter>
|
using token_it = std::vector<std::string>::const_iterator;
|
||||||
template_builder::get_template_argument_from_type_parameter_string(
|
|
||||||
const clang::Decl *decl, const std::string &type_name) const
|
namespace detail {
|
||||||
|
|
||||||
|
std::string map_type_parameter_to_template_parameter(
|
||||||
|
const clang::ClassTemplateSpecializationDecl *decl, const std::string &tp)
|
||||||
|
{
|
||||||
|
const auto [depth0, index0, qualifier0] =
|
||||||
|
common::extract_template_parameter_index(tp);
|
||||||
|
|
||||||
|
for (auto i = 0U; i < decl->getDescribedTemplateParams()->size(); i++) {
|
||||||
|
const auto *param = decl->getDescribedTemplateParams()->getParam(i);
|
||||||
|
|
||||||
|
if (i == index0) {
|
||||||
|
return param->getNameAsString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tp;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string map_type_parameter_to_template_parameter(
|
||||||
|
const clang::TypeAliasTemplateDecl *decl, const std::string &tp)
|
||||||
|
{
|
||||||
|
const auto [depth0, index0, qualifier0] =
|
||||||
|
common::extract_template_parameter_index(tp);
|
||||||
|
|
||||||
|
for (auto i = 0U; i < decl->getTemplateParameters()->size(); i++) {
|
||||||
|
const auto *param = decl->getTemplateParameters()->getParam(i);
|
||||||
|
|
||||||
|
if (i == index0) {
|
||||||
|
return param->getNameAsString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tp;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template_parameter map_type_parameter_to_template_parameter(
|
||||||
|
const clang::Decl *decl, const std::string &tp)
|
||||||
{
|
{
|
||||||
if (const auto *template_decl =
|
if (const auto *template_decl =
|
||||||
llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(decl);
|
llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(decl);
|
||||||
template_decl != nullptr && type_name.find("type-parameter-") == 0) {
|
template_decl != nullptr && tp.find("type-parameter-") == 0) {
|
||||||
|
return template_parameter::make_template_type(
|
||||||
if (type_name.rfind("type-parameter-") > 0 &&
|
detail::map_type_parameter_to_template_parameter(
|
||||||
type_name.find("::*") != std::string::npos) {
|
template_decl, tp));
|
||||||
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(' ')));
|
|
||||||
|
|
||||||
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) {
|
if (const auto *alias_decl =
|
||||||
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);
|
llvm::dyn_cast<clang::TypeAliasTemplateDecl>(decl);
|
||||||
alias_decl != nullptr && type_name.find("type-parameter-") == 0) {
|
alias_decl != nullptr &&
|
||||||
const auto [depth, index, qualifier] =
|
(tp.find("type-parameter-") != std::string::npos)) {
|
||||||
common::extract_template_parameter_index(type_name);
|
return template_parameter::make_template_type(
|
||||||
|
detail::map_type_parameter_to_template_parameter(alias_decl, tp));
|
||||||
|
}
|
||||||
|
|
||||||
std::string param_name = type_name;
|
return template_parameter::make_argument(tp);
|
||||||
|
}
|
||||||
|
|
||||||
const auto *template_decl_at_depth = alias_decl;
|
std::optional<template_parameter> build_template_parameter(
|
||||||
|
const clang::Decl *decl, token_it begin, token_it end)
|
||||||
|
{
|
||||||
|
if (decl == nullptr)
|
||||||
|
return {};
|
||||||
|
|
||||||
template_decl_at_depth->dump();
|
auto res = template_parameter::make_template_type({});
|
||||||
|
|
||||||
for (auto i = 0U;
|
std::string param_qualifier{}; // e.g. const& or &&
|
||||||
i < template_decl_at_depth->getTemplateParameters()->size(); i++) {
|
|
||||||
const auto *param =
|
|
||||||
template_decl_at_depth->getTemplateParameters()->getParam(i);
|
|
||||||
|
|
||||||
if (i == index) {
|
auto it = begin;
|
||||||
param_name = param->getNameAsString();
|
auto it_next = it;
|
||||||
|
it_next++;
|
||||||
|
|
||||||
auto template_param =
|
if (*it == "const") {
|
||||||
template_parameter::make_template_type(param_name);
|
param_qualifier = "const";
|
||||||
|
it++;
|
||||||
|
it_next++;
|
||||||
|
}
|
||||||
|
|
||||||
template_param.is_variadic(param->isParameterPack());
|
if (it == end)
|
||||||
|
return {};
|
||||||
|
|
||||||
return template_param;
|
// simple template param without qualifiers
|
||||||
|
if (common::is_type_token(*it) && it_next == end) {
|
||||||
|
res = map_type_parameter_to_template_parameter(decl, *it);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
// template parameter with qualifier at the end
|
||||||
|
else if (common::is_type_token(*it) && common::is_qualifier(*it_next)) {
|
||||||
|
res = map_type_parameter_to_template_parameter(decl, *it);
|
||||||
|
res.set_qualifier(*it_next);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
// method template parameter
|
||||||
|
else if (common::is_type_token(*it) && common::is_type_token(*it_next)) {
|
||||||
|
res.add_template_param(
|
||||||
|
map_type_parameter_to_template_parameter(decl, *it));
|
||||||
|
res.add_template_param(
|
||||||
|
map_type_parameter_to_template_parameter(decl, *it_next));
|
||||||
|
it = it_next;
|
||||||
|
it++;
|
||||||
|
if (it != end && *it == "::") {
|
||||||
|
res.set_method_template(true);
|
||||||
|
it++;
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it != end && common::is_qualifier(*it)) {
|
||||||
|
res.set_qualifier(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
else if (common::is_type_token(*it) && *it_next == "(") {
|
||||||
|
res.add_template_param(
|
||||||
|
map_type_parameter_to_template_parameter(decl, *it));
|
||||||
|
it_next++;
|
||||||
|
res.add_template_param(
|
||||||
|
map_type_parameter_to_template_parameter(decl, *it_next));
|
||||||
|
|
||||||
|
it = it_next;
|
||||||
|
it++;
|
||||||
|
if (*it == "::") {
|
||||||
|
res.set_method_template(true);
|
||||||
|
it++;
|
||||||
|
it++;
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it != end) {
|
||||||
|
// handle args
|
||||||
|
if (*it == "(") {
|
||||||
|
it++;
|
||||||
|
while (true) {
|
||||||
|
auto arg_separator = std::find(it, end, ",");
|
||||||
|
if (arg_separator == end) {
|
||||||
|
// just one arg
|
||||||
|
auto args_end = std::find(it, end, ")");
|
||||||
|
auto arg = build_template_parameter(decl, it, args_end);
|
||||||
|
if (arg)
|
||||||
|
res.add_template_param(*arg);
|
||||||
|
it++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto arg =
|
||||||
|
build_template_parameter(decl, it, arg_separator);
|
||||||
|
if (arg)
|
||||||
|
res.add_template_param(*arg);
|
||||||
|
it = arg_separator;
|
||||||
|
it++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(*it == ")");
|
||||||
|
|
||||||
|
it++;
|
||||||
|
|
||||||
|
if (it != end && common::is_qualifier(*it)) {
|
||||||
|
res.set_qualifier(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<template_parameter>
|
||||||
|
template_builder::get_template_argument_from_type_parameter_string(
|
||||||
|
const clang::Decl *decl, std::string type_name) const
|
||||||
|
{
|
||||||
|
type_name = util::trim(type_name);
|
||||||
|
|
||||||
|
auto toks = common::tokenize_unexposed_template_parameter(type_name);
|
||||||
|
|
||||||
|
return build_template_parameter(decl, toks.begin(), toks.end());
|
||||||
|
}
|
||||||
|
|
||||||
template_parameter template_builder::process_integral_argument(
|
template_parameter template_builder::process_integral_argument(
|
||||||
const clang::TemplateArgument &arg)
|
const clang::TemplateArgument &arg)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ public:
|
|||||||
|
|
||||||
std::optional<template_parameter>
|
std::optional<template_parameter>
|
||||||
get_template_argument_from_type_parameter_string(
|
get_template_argument_from_type_parameter_string(
|
||||||
const clang::Decl *decl, const std::string &type_name) const;
|
const clang::Decl *decl, std::string type_name) const;
|
||||||
|
|
||||||
common::visitor::ast_id_mapper &id_mapper();
|
common::visitor::ast_id_mapper &id_mapper();
|
||||||
|
|
||||||
|
|||||||
@@ -210,11 +210,15 @@ bool translation_unit_visitor::VisitClassTemplateSpecializationDecl(
|
|||||||
// Process template specialization bases
|
// Process template specialization bases
|
||||||
process_class_bases(cls, template_specialization);
|
process_class_bases(cls, template_specialization);
|
||||||
|
|
||||||
|
if (!template_specialization.template_specialization_found()) {
|
||||||
|
// Only do this if we haven't found a bettern specialization during
|
||||||
|
// construction of the template specialization
|
||||||
const auto maybe_id =
|
const auto maybe_id =
|
||||||
id_mapper().get_global_id(cls->getSpecializedTemplate()->getID());
|
id_mapper().get_global_id(cls->getSpecializedTemplate()->getID());
|
||||||
if (maybe_id.has_value())
|
if (maybe_id.has_value())
|
||||||
template_specialization.add_relationship(
|
template_specialization.add_relationship(
|
||||||
{relationship_t::kInstantiation, maybe_id.value()});
|
{relationship_t::kInstantiation, maybe_id.value()});
|
||||||
|
}
|
||||||
|
|
||||||
if (diagram_.should_include(template_specialization)) {
|
if (diagram_.should_include(template_specialization)) {
|
||||||
const auto full_name = template_specialization.full_name(false);
|
const auto full_name = template_specialization.full_name(false);
|
||||||
|
|||||||
@@ -459,4 +459,125 @@ std::vector<common::model::template_parameter> parse_unexposed_template_params(
|
|||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_type_parameter(const std::string &t)
|
||||||
|
{
|
||||||
|
return t.find("type-parameter-") == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_qualifier(const std::string &q)
|
||||||
|
{
|
||||||
|
return q == "&" || q == "&&" || q == "const&";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_bracket(const std::string &b)
|
||||||
|
{
|
||||||
|
return b == "(" || b == ")" || b == "[" || b == "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_identifier_character(char c) { return std::isalnum(c) || c == '_'; }
|
||||||
|
|
||||||
|
bool is_identifier(const std::string &t)
|
||||||
|
{
|
||||||
|
return std::isalpha(t.at(0)) &&
|
||||||
|
std::all_of(t.begin(), t.end(),
|
||||||
|
[](const char c) { return is_identifier_character(c); });
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_keyword(const std::string &t)
|
||||||
|
{
|
||||||
|
static std::vector<std::string> keywords {"alignas", "alignof", "asm",
|
||||||
|
"auto", "bool", "break", "case", "catch", "char", "char16_t",
|
||||||
|
"char32_t", "class", "concept", "const", "constexpr", "const_cast",
|
||||||
|
"continue", "decltype", "default", "delete", "do", "double",
|
||||||
|
"dynamic_cast", "else", "enum", "explicit", "export", "extern", "false",
|
||||||
|
"float", "for", "friend", "goto", "if", "inline", "int", "long",
|
||||||
|
"mutable", "namespace", "new", "noexcept", "nullptr", "operator",
|
||||||
|
"private", "protected", "public", "register", "reinterpret_cast",
|
||||||
|
"return", "requires", "short", "signed", "sizeof", "static",
|
||||||
|
"static_assert", "static_cast", "struct", "switch", "template", "this",
|
||||||
|
"thread_local", "throw", "true", "try", "typedef", "typeid", "typename",
|
||||||
|
"union", "unsigned", "using", "virtual", "void", "volatile", "wchar_t",
|
||||||
|
"while"};
|
||||||
|
|
||||||
|
return util::contains(keywords, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_qualified_identifier(const std::string &t)
|
||||||
|
{
|
||||||
|
return std::isalpha(t.at(0)) &&
|
||||||
|
std::all_of(t.begin(), t.end(), [](const char c) {
|
||||||
|
return is_identifier_character(c) || c == ':';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_type_token(const std::string &t)
|
||||||
|
{
|
||||||
|
return is_type_parameter(t) ||
|
||||||
|
(is_identifier(t) && !is_qualifier(t) && !is_bracket(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> tokenize_unexposed_template_parameter(
|
||||||
|
const std::string &t)
|
||||||
|
{
|
||||||
|
std::vector<std::string> result;
|
||||||
|
|
||||||
|
auto spaced_out = util::split(t, " ");
|
||||||
|
|
||||||
|
for (const auto &word : spaced_out) {
|
||||||
|
if (is_qualified_identifier(word)) {
|
||||||
|
if (word != "class" && word != "templated" && word != "struct")
|
||||||
|
result.push_back(word);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string tok;
|
||||||
|
|
||||||
|
for (const char c : word) {
|
||||||
|
if (c == '(' || c == ')' || c == '[' || c == ']') {
|
||||||
|
if (!tok.empty())
|
||||||
|
result.push_back(tok);
|
||||||
|
result.push_back(std::string{c});
|
||||||
|
tok.clear();
|
||||||
|
}
|
||||||
|
else if (c == ':') {
|
||||||
|
if (!tok.empty() && tok != ":") {
|
||||||
|
result.push_back(tok);
|
||||||
|
tok = ":";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tok += ':';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (c == ',') {
|
||||||
|
if (!tok.empty()) {
|
||||||
|
result.push_back(tok);
|
||||||
|
}
|
||||||
|
result.push_back(",");
|
||||||
|
tok.clear();
|
||||||
|
}
|
||||||
|
else if (c == '*') {
|
||||||
|
if (!tok.empty()) {
|
||||||
|
result.push_back(tok);
|
||||||
|
}
|
||||||
|
result.push_back("*");
|
||||||
|
tok.clear();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tok += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tok = util::trim(tok);
|
||||||
|
|
||||||
|
if (!tok.empty()) {
|
||||||
|
if (tok != "class" && tok != "typename" && word != "struct")
|
||||||
|
result.push_back(tok);
|
||||||
|
tok.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace clanguml::common
|
} // namespace clanguml::common
|
||||||
|
|||||||
@@ -153,6 +153,9 @@ std::vector<common::model::template_parameter> parse_unexposed_template_params(
|
|||||||
const std::function<std::string(const std::string &)> &ns_resolve,
|
const std::function<std::string(const std::string &)> &ns_resolve,
|
||||||
int depth = 0);
|
int depth = 0);
|
||||||
|
|
||||||
|
std::vector<std::string> tokenize_unexposed_template_parameter(
|
||||||
|
const std::string &t);
|
||||||
|
|
||||||
template <typename T, typename P, typename F>
|
template <typename T, typename P, typename F>
|
||||||
void if_dyn_cast(P pointer, F &&func)
|
void if_dyn_cast(P pointer, F &&func)
|
||||||
{
|
{
|
||||||
@@ -164,4 +167,19 @@ void if_dyn_cast(P pointer, F &&func)
|
|||||||
std::forward<F>(func)(dyn_cast_value);
|
std::forward<F>(func)(dyn_cast_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_type_parameter(const std::string &t);
|
||||||
|
|
||||||
|
bool is_qualifier(const std::string &q);
|
||||||
|
|
||||||
|
bool is_bracket(const std::string &b);
|
||||||
|
|
||||||
|
bool is_identifier_character(char c);
|
||||||
|
|
||||||
|
bool is_identifier(const std::string &t);
|
||||||
|
|
||||||
|
bool is_qualified_identifier(const std::string &t);
|
||||||
|
|
||||||
|
bool is_type_token(const std::string &t);
|
||||||
|
|
||||||
} // namespace clanguml::common
|
} // namespace clanguml::common
|
||||||
|
|||||||
@@ -113,6 +113,20 @@ int template_parameter::calculate_specialization_match(
|
|||||||
{
|
{
|
||||||
int res{0};
|
int res{0};
|
||||||
|
|
||||||
|
if (qualifier() != base_template_parameter.qualifier())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (is_template_parameter() &&
|
||||||
|
base_template_parameter.is_template_parameter() &&
|
||||||
|
template_params().empty() &&
|
||||||
|
base_template_parameter.template_params().empty() &&
|
||||||
|
is_variadic() == is_variadic() &&
|
||||||
|
is_function_template() ==
|
||||||
|
base_template_parameter.is_function_template() &&
|
||||||
|
is_method_template() == base_template_parameter.is_method_template()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
auto maybe_base_template_parameter_type = base_template_parameter.type();
|
auto maybe_base_template_parameter_type = base_template_parameter.type();
|
||||||
auto maybe_template_parameter_type = type();
|
auto maybe_template_parameter_type = type();
|
||||||
|
|
||||||
@@ -132,6 +146,9 @@ int template_parameter::calculate_specialization_match(
|
|||||||
!is_function_template())
|
!is_function_template())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (base_template_parameter.is_method_template() && !is_method_template())
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (!base_template_parameter.template_params().empty() &&
|
if (!base_template_parameter.template_params().empty() &&
|
||||||
!template_params().empty()) {
|
!template_params().empty()) {
|
||||||
auto params_match = calculate_template_params_specialization_match(
|
auto params_match = calculate_template_params_specialization_match(
|
||||||
@@ -216,10 +233,29 @@ std::string template_parameter::to_string(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (is_method_template()) {
|
if (is_method_template()) {
|
||||||
assert(template_params().size() == 2);
|
assert(template_params().size() > 1);
|
||||||
|
|
||||||
return fmt::format("{} {}::*{}", template_params().at(0).name().value(),
|
if (template_params().size() == 2) {
|
||||||
template_params().at(1).name().value(), method_qualifier());
|
return fmt::format("{} {}::*{}",
|
||||||
|
template_params().at(0).to_string(using_namespace, relative),
|
||||||
|
template_params().at(1).to_string(using_namespace, relative),
|
||||||
|
qualifier());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto it = template_params().begin();
|
||||||
|
auto return_type = it->to_string(using_namespace, relative);
|
||||||
|
it++;
|
||||||
|
auto class_type = it->to_string(using_namespace, relative);
|
||||||
|
it++;
|
||||||
|
std::vector<std::string> args;
|
||||||
|
|
||||||
|
for (; it != template_params().end(); it++) {
|
||||||
|
args.push_back(it->to_string(using_namespace, relative));
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt::format("{} ({}::*)({}){}", return_type, class_type,
|
||||||
|
fmt::join(args, ","), qualifier());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string res;
|
std::string res;
|
||||||
@@ -271,6 +307,9 @@ std::string template_parameter::to_string(
|
|||||||
res += fmt::format("<{}>", fmt::join(params, ","));
|
res += fmt::format("<{}>", fmt::join(params, ","));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!qualifier().empty())
|
||||||
|
res += " " + qualifier();
|
||||||
|
|
||||||
const auto &maybe_default_value = default_value();
|
const auto &maybe_default_value = default_value();
|
||||||
if (maybe_default_value) {
|
if (maybe_default_value) {
|
||||||
res += "=";
|
res += "=";
|
||||||
@@ -342,6 +381,12 @@ int calculate_template_params_specialization_match(
|
|||||||
{
|
{
|
||||||
int res{0};
|
int res{0};
|
||||||
|
|
||||||
|
if (specialization_params.size() != template_params.size() &&
|
||||||
|
!std::any_of(template_params.begin(), template_params.end(),
|
||||||
|
[](const auto &t) { return t.is_variadic(); })) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (!specialization_params.empty() && !template_params.empty()) {
|
if (!specialization_params.empty() && !template_params.empty()) {
|
||||||
auto template_index{0U};
|
auto template_index{0U};
|
||||||
auto arg_index{0U};
|
auto arg_index{0U};
|
||||||
|
|||||||
@@ -186,9 +186,9 @@ public:
|
|||||||
|
|
||||||
bool is_method_template() const { return is_method_template_; }
|
bool is_method_template() const { return is_method_template_; }
|
||||||
|
|
||||||
void set_method_qualifier(const std::string &q) { method_qualifier_ = q; }
|
void set_qualifier(const std::string &q) { qualifier_ = q; }
|
||||||
|
|
||||||
const std::string &method_qualifier() const { return method_qualifier_; }
|
const std::string &qualifier() const { return qualifier_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template_parameter() = default;
|
template_parameter() = default;
|
||||||
@@ -220,7 +220,7 @@ private:
|
|||||||
|
|
||||||
bool is_method_template_{false};
|
bool is_method_template_{false};
|
||||||
|
|
||||||
std::string method_qualifier_;
|
std::string qualifier_;
|
||||||
|
|
||||||
/// Stores optional fully qualified name of constraint for this template
|
/// Stores optional fully qualified name of constraint for this template
|
||||||
/// parameter
|
/// parameter
|
||||||
|
|||||||
@@ -301,6 +301,7 @@ using namespace clanguml::test::matchers;
|
|||||||
#endif
|
#endif
|
||||||
#include "t00060/test_case.h"
|
#include "t00060/test_case.h"
|
||||||
#include "t00061/test_case.h"
|
#include "t00061/test_case.h"
|
||||||
|
#include "t00062/test_case.h"
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Sequence diagram tests
|
/// Sequence diagram tests
|
||||||
|
|||||||
@@ -286,4 +286,38 @@ TEST_CASE(
|
|||||||
|
|
||||||
CHECK(sink_s.calculate_specialization_match(sink_t));
|
CHECK(sink_s.calculate_specialization_match(sink_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto tp1 = template_parameter::make_template_type({});
|
||||||
|
tp1.set_method_template(true);
|
||||||
|
tp1.add_template_param(template_parameter::make_template_type("Ret"));
|
||||||
|
tp1.add_template_param(template_parameter::make_template_type("C"));
|
||||||
|
tp1.add_template_param(
|
||||||
|
template_parameter::make_template_type("Arg0"));
|
||||||
|
|
||||||
|
auto tp2 = template_parameter::make_template_type({});
|
||||||
|
tp2.set_method_template(true);
|
||||||
|
tp2.add_template_param(template_parameter::make_argument("char"));
|
||||||
|
tp2.add_template_param(template_parameter::make_template_type("C"));
|
||||||
|
tp2.add_template_param(template_parameter::make_argument("double"));
|
||||||
|
|
||||||
|
CHECK(tp2.calculate_specialization_match(tp1));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto tp1 = template_parameter::make_template_type({});
|
||||||
|
tp1.set_method_template(true);
|
||||||
|
tp1.add_template_param(template_parameter::make_template_type("Ret"));
|
||||||
|
tp1.add_template_param(template_parameter::make_template_type("C"));
|
||||||
|
tp1.add_template_param(
|
||||||
|
template_parameter::make_template_type("Arg0"));
|
||||||
|
|
||||||
|
auto tp2 = template_parameter::make_template_type({});
|
||||||
|
tp2.set_method_template(true);
|
||||||
|
tp2.add_template_param(template_parameter::make_argument("char"));
|
||||||
|
tp2.add_template_param(template_parameter::make_template_type("C"));
|
||||||
|
tp2.add_template_param(template_parameter::make_argument("double"));
|
||||||
|
|
||||||
|
CHECK(tp2.calculate_specialization_match(tp1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -285,3 +285,107 @@ TEST_CASE("Test hash_seed", "[unit-test]")
|
|||||||
CHECK(hash_seed(1) == hash_seed(1));
|
CHECK(hash_seed(1) == hash_seed(1));
|
||||||
CHECK(hash_seed(1) != hash_seed(2));
|
CHECK(hash_seed(1) != hash_seed(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test tokenize_unexposed_template_parameter", "[unit-test]")
|
||||||
|
{
|
||||||
|
using namespace clanguml::common;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto r = tokenize_unexposed_template_parameter("type-parameter-0-1");
|
||||||
|
CHECK(r[0] == "type-parameter-0-1");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
auto r =
|
||||||
|
tokenize_unexposed_template_parameter("const type-parameter-0-1 &");
|
||||||
|
CHECK(r[i++] == "const");
|
||||||
|
CHECK(r[i++] == "type-parameter-0-1");
|
||||||
|
CHECK(r[i++] == "&");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
auto r = tokenize_unexposed_template_parameter(
|
||||||
|
"const type-parameter-0-0 type-parameter-0-1::* &&");
|
||||||
|
CHECK(r[i++] == "const");
|
||||||
|
CHECK(r[i++] == "type-parameter-0-0");
|
||||||
|
CHECK(r[i++] == "type-parameter-0-1");
|
||||||
|
CHECK(r[i++] == "::");
|
||||||
|
CHECK(r[i++] == "*");
|
||||||
|
CHECK(r[i++] == "&&");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
auto r = tokenize_unexposed_template_parameter(
|
||||||
|
"type-parameter-0-0 [type-parameter-0-1]");
|
||||||
|
CHECK(r[i++] == "type-parameter-0-0");
|
||||||
|
CHECK(r[i++] == "[");
|
||||||
|
CHECK(r[i++] == "type-parameter-0-1");
|
||||||
|
CHECK(r[i++] == "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
auto r = tokenize_unexposed_template_parameter(
|
||||||
|
"type-parameter-0-0 (type-parameter-0-1::*)(type-parameter-0-2, "
|
||||||
|
"type-parameter-0-3, type-parameter-0-4)");
|
||||||
|
CHECK(r[i++] == "type-parameter-0-0");
|
||||||
|
CHECK(r[i++] == "(");
|
||||||
|
CHECK(r[i++] == "type-parameter-0-1");
|
||||||
|
CHECK(r[i++] == "::");
|
||||||
|
CHECK(r[i++] == "*");
|
||||||
|
CHECK(r[i++] == ")");
|
||||||
|
CHECK(r[i++] == "(");
|
||||||
|
CHECK(r[i++] == "type-parameter-0-2");
|
||||||
|
CHECK(r[i++] == ",");
|
||||||
|
CHECK(r[i++] == "type-parameter-0-3");
|
||||||
|
CHECK(r[i++] == ",");
|
||||||
|
CHECK(r[i++] == "type-parameter-0-4");
|
||||||
|
CHECK(r[i++] == ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
auto r = tokenize_unexposed_template_parameter(
|
||||||
|
"const ns1::ns2::A &");
|
||||||
|
CHECK(r[i++] == "const");
|
||||||
|
CHECK(r[i++] == "ns1::ns2::A");
|
||||||
|
CHECK(r[i++] == "&");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
auto r = tokenize_unexposed_template_parameter(
|
||||||
|
"class ns1::ns2::A &");
|
||||||
|
CHECK(r[i++] == "ns1::ns2::A");
|
||||||
|
CHECK(r[i++] == "&");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
auto r = tokenize_unexposed_template_parameter(
|
||||||
|
"ns1::ns2::A C::* &&");
|
||||||
|
CHECK(r[i++] == "ns1::ns2::A");
|
||||||
|
CHECK(r[i++] == "C");
|
||||||
|
CHECK(r[i++] == "::");
|
||||||
|
CHECK(r[i++] == "*");
|
||||||
|
CHECK(r[i++] == "&&");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
auto r = tokenize_unexposed_template_parameter(
|
||||||
|
"unsigned int");
|
||||||
|
CHECK(r[i++] == "unsigned");
|
||||||
|
CHECK(r[i++] == "int");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user