Fixed handling of decltype cxxmember type aliases with dependend parameters
This commit is contained in:
@@ -452,10 +452,11 @@ void template_builder::argument_process_dispatch(
|
|||||||
case clang::TemplateArgument::Template:
|
case clang::TemplateArgument::Template:
|
||||||
argument.push_back(process_template_argument(arg));
|
argument.push_back(process_template_argument(arg));
|
||||||
break;
|
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));
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case clang::TemplateArgument::Declaration:
|
case clang::TemplateArgument::Declaration:
|
||||||
break;
|
break;
|
||||||
case clang::TemplateArgument::NullPtr:
|
case clang::TemplateArgument::NullPtr:
|
||||||
@@ -509,79 +510,11 @@ template_parameter template_builder::process_template_expansion(
|
|||||||
clang::QualType template_builder::consume_context(
|
clang::QualType template_builder::consume_context(
|
||||||
clang::QualType type, template_parameter &tp) const
|
clang::QualType type, template_parameter &tp) const
|
||||||
{
|
{
|
||||||
while (true) {
|
auto [unqualified_type, context] = common::consume_type_context(type);
|
||||||
bool try_again{false};
|
|
||||||
common::model::context ctx;
|
|
||||||
|
|
||||||
if (type->isPointerType() || type->isReferenceType()) {
|
tp.deduced_context(std::move(context));
|
||||||
if (type.isConstQualified() || type.isVolatileQualified()) {
|
|
||||||
ctx.is_ref_const = type.isConstQualified();
|
|
||||||
ctx.is_ref_volatile = type.isVolatileQualified();
|
|
||||||
|
|
||||||
try_again = true;
|
return unqualified_type;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type->isLValueReferenceType()) {
|
|
||||||
ctx.pr = common::model::rpqualifier::kLValueReference;
|
|
||||||
try_again = true;
|
|
||||||
}
|
|
||||||
else if (type->isRValueReferenceType()) {
|
|
||||||
ctx.pr = common::model::rpqualifier::kRValueReference;
|
|
||||||
try_again = true;
|
|
||||||
}
|
|
||||||
else if (type->isMemberFunctionPointerType() &&
|
|
||||||
type->getPointeeType()->getAs<clang::FunctionProtoType>() !=
|
|
||||||
nullptr) {
|
|
||||||
const auto ref_qualifier =
|
|
||||||
type->getPointeeType() // NOLINT
|
|
||||||
->getAs<clang::FunctionProtoType>() // NOLINT
|
|
||||||
->getRefQualifier();
|
|
||||||
|
|
||||||
if (ref_qualifier == clang::RefQualifierKind::RQ_RValue) {
|
|
||||||
ctx.pr = common::model::rpqualifier::kRValueReference;
|
|
||||||
try_again = true;
|
|
||||||
}
|
|
||||||
else if (ref_qualifier == clang::RefQualifierKind::RQ_LValue) {
|
|
||||||
ctx.pr = common::model::rpqualifier::kLValueReference;
|
|
||||||
try_again = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (type->isPointerType()) {
|
|
||||||
ctx.pr = common::model::rpqualifier::kPointer;
|
|
||||||
try_again = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (try_again) {
|
|
||||||
if (type->isPointerType()) {
|
|
||||||
if (type->getPointeeType().isConstQualified())
|
|
||||||
ctx.is_const = true;
|
|
||||||
if (type->getPointeeType().isVolatileQualified())
|
|
||||||
ctx.is_volatile = true;
|
|
||||||
|
|
||||||
type = type->getPointeeType().getUnqualifiedType();
|
|
||||||
}
|
|
||||||
else if (type->isReferenceType()) {
|
|
||||||
if (type.getNonReferenceType().isConstQualified())
|
|
||||||
ctx.is_const = true;
|
|
||||||
if (type.getNonReferenceType().isVolatileQualified())
|
|
||||||
ctx.is_volatile = true;
|
|
||||||
|
|
||||||
type = type.getNonReferenceType().getUnqualifiedType();
|
|
||||||
}
|
|
||||||
else if (type.isConstQualified() || type.isVolatileQualified()) {
|
|
||||||
ctx.is_const = type.isConstQualified();
|
|
||||||
ctx.is_volatile = type.isVolatileQualified();
|
|
||||||
}
|
|
||||||
|
|
||||||
tp.push_context(ctx);
|
|
||||||
|
|
||||||
if (type->isMemberFunctionPointerType())
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template_parameter template_builder::process_type_argument(
|
template_parameter template_builder::process_type_argument(
|
||||||
@@ -626,6 +559,16 @@ template_parameter template_builder::process_type_argument(
|
|||||||
if (argument)
|
if (argument)
|
||||||
return *argument;
|
return *argument;
|
||||||
|
|
||||||
|
argument = try_as_decl_type(parent, cls, template_decl, type,
|
||||||
|
template_instantiation, argument_index);
|
||||||
|
if (argument)
|
||||||
|
return *argument;
|
||||||
|
|
||||||
|
argument = try_as_typedef_type(parent, cls, template_decl, type,
|
||||||
|
template_instantiation, argument_index);
|
||||||
|
if (argument)
|
||||||
|
return *argument;
|
||||||
|
|
||||||
argument = try_as_lambda(cls, template_decl, type);
|
argument = try_as_lambda(cls, template_decl, type);
|
||||||
if (argument)
|
if (argument)
|
||||||
return *argument;
|
return *argument;
|
||||||
@@ -940,6 +883,8 @@ std::optional<template_parameter> template_builder::try_as_function_prototype(
|
|||||||
if (function_type == nullptr)
|
if (function_type == nullptr)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
LOG_DBG("Template argument is a function prototype");
|
||||||
|
|
||||||
auto argument = template_parameter::make_template_type("");
|
auto argument = template_parameter::make_template_type("");
|
||||||
|
|
||||||
type = consume_context(type, argument);
|
type = consume_context(type, argument);
|
||||||
@@ -969,6 +914,57 @@ std::optional<template_parameter> template_builder::try_as_function_prototype(
|
|||||||
return argument;
|
return argument;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<template_parameter> template_builder::try_as_decl_type(
|
||||||
|
std::optional<clanguml::class_diagram::model::class_ *> &parent,
|
||||||
|
const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
|
||||||
|
clang::QualType &type, class_ &template_instantiation,
|
||||||
|
size_t argument_index)
|
||||||
|
{
|
||||||
|
const auto *decl_type =
|
||||||
|
common::dereference(type)->getAs<clang::DecltypeType>();
|
||||||
|
if (decl_type == nullptr) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Template argument is a decltype()");
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<template_parameter> template_builder::try_as_typedef_type(
|
||||||
|
std::optional<clanguml::class_diagram::model::class_ *> &parent,
|
||||||
|
const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
|
||||||
|
clang::QualType &type, class_ &template_instantiation,
|
||||||
|
size_t argument_index)
|
||||||
|
{
|
||||||
|
const auto *typedef_type =
|
||||||
|
common::dereference(type)->getAs<clang::TypedefType>();
|
||||||
|
if (typedef_type == nullptr) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Template argument is a typedef/using");
|
||||||
|
|
||||||
|
// If this is a typedef/using alias to a decltype - we're not able
|
||||||
|
// to figure out anything out of it probably
|
||||||
|
if (typedef_type->getAs<clang::DecltypeType>() != nullptr) {
|
||||||
|
// Here we need to figure out the parent context of this alias,
|
||||||
|
// it can be a:
|
||||||
|
// - class/struct
|
||||||
|
if (typedef_type->getDecl()->isCXXClassMember() && parent) {
|
||||||
|
return template_parameter::make_argument(
|
||||||
|
fmt::format("{}::{}", parent.value()->full_name(false),
|
||||||
|
typedef_type->getDecl()->getNameAsString()));
|
||||||
|
}
|
||||||
|
// - namespace
|
||||||
|
return template_parameter::make_argument(
|
||||||
|
typedef_type->getDecl()->getQualifiedNameAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<template_parameter>
|
std::optional<template_parameter>
|
||||||
template_builder::try_as_template_specialization_type(
|
template_builder::try_as_template_specialization_type(
|
||||||
std::optional<clanguml::class_diagram::model::class_ *> &parent,
|
std::optional<clanguml::class_diagram::model::class_ *> &parent,
|
||||||
@@ -978,8 +974,11 @@ template_builder::try_as_template_specialization_type(
|
|||||||
{
|
{
|
||||||
const auto *nested_template_type =
|
const auto *nested_template_type =
|
||||||
common::dereference(type)->getAs<clang::TemplateSpecializationType>();
|
common::dereference(type)->getAs<clang::TemplateSpecializationType>();
|
||||||
if (nested_template_type == nullptr)
|
if (nested_template_type == nullptr) {
|
||||||
return {};
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Template argument is a 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);
|
||||||
@@ -1074,6 +1073,8 @@ std::optional<template_parameter> template_builder::try_as_template_parm_type(
|
|||||||
if (type_parameter == nullptr)
|
if (type_parameter == nullptr)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
LOG_DBG("Template argument is a template parameter type");
|
||||||
|
|
||||||
auto argument = template_parameter::make_template_type("");
|
auto argument = template_parameter::make_template_type("");
|
||||||
type = consume_context(type, argument);
|
type = consume_context(type, argument);
|
||||||
|
|
||||||
@@ -1100,6 +1101,8 @@ std::optional<template_parameter> template_builder::try_as_lambda(
|
|||||||
if (type_name.find("(lambda at ") != 0)
|
if (type_name.find("(lambda at ") != 0)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
LOG_DBG("Template argument is a lambda");
|
||||||
|
|
||||||
auto argument = template_parameter::make_argument("");
|
auto argument = template_parameter::make_argument("");
|
||||||
type = consume_context(type, argument);
|
type = consume_context(type, argument);
|
||||||
|
|
||||||
@@ -1120,6 +1123,8 @@ std::optional<template_parameter> template_builder::try_as_record_type(
|
|||||||
if (record_type == nullptr)
|
if (record_type == nullptr)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
LOG_DBG("Template argument is a c++ record");
|
||||||
|
|
||||||
auto argument = template_parameter::make_argument({});
|
auto argument = template_parameter::make_argument({});
|
||||||
type = consume_context(type, argument);
|
type = consume_context(type, argument);
|
||||||
|
|
||||||
@@ -1176,6 +1181,8 @@ std::optional<template_parameter> template_builder::try_as_enum_type(
|
|||||||
if (enum_type == nullptr)
|
if (enum_type == nullptr)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
LOG_DBG("Template argument is a an enum");
|
||||||
|
|
||||||
auto argument = template_parameter::make_argument({});
|
auto argument = template_parameter::make_argument({});
|
||||||
type = consume_context(type, argument);
|
type = consume_context(type, argument);
|
||||||
|
|
||||||
@@ -1200,6 +1207,8 @@ std::optional<template_parameter> template_builder::try_as_builtin_type(
|
|||||||
if (builtin_type == nullptr)
|
if (builtin_type == nullptr)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
LOG_DBG("Template argument is a builtin type");
|
||||||
|
|
||||||
auto type_name = common::to_string(type, template_decl->getASTContext());
|
auto type_name = common::to_string(type, template_decl->getASTContext());
|
||||||
auto argument = template_parameter::make_argument(type_name);
|
auto argument = template_parameter::make_argument(type_name);
|
||||||
|
|
||||||
|
|||||||
@@ -157,6 +157,18 @@ public:
|
|||||||
clang::QualType &type, class_ &template_instantiation,
|
clang::QualType &type, class_ &template_instantiation,
|
||||||
size_t argument_index);
|
size_t argument_index);
|
||||||
|
|
||||||
|
std::optional<template_parameter> try_as_decl_type(
|
||||||
|
std::optional<clanguml::class_diagram::model::class_ *> &parent,
|
||||||
|
const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
|
||||||
|
clang::QualType &type, class_ &template_instantiation,
|
||||||
|
size_t argument_index);
|
||||||
|
|
||||||
|
std::optional<template_parameter> try_as_typedef_type(
|
||||||
|
std::optional<clanguml::class_diagram::model::class_ *> &parent,
|
||||||
|
const clang::NamedDecl *cls, const clang::TemplateDecl *template_decl,
|
||||||
|
clang::QualType &type, class_ &template_instantiation,
|
||||||
|
size_t argument_index);
|
||||||
|
|
||||||
clang::QualType consume_context(
|
clang::QualType consume_context(
|
||||||
clang::QualType type, template_parameter &tp) const;
|
clang::QualType type, template_parameter &tp) const;
|
||||||
|
|
||||||
|
|||||||
@@ -1291,6 +1291,30 @@ void translation_unit_visitor::process_method(
|
|||||||
// find relationship for return type
|
// find relationship for return type
|
||||||
found_relationships_t relationships;
|
found_relationships_t relationships;
|
||||||
|
|
||||||
|
// Move dereferencing to build() method of template_builder
|
||||||
|
if (const auto *templ = mf.getReturnType()
|
||||||
|
.getNonReferenceType()
|
||||||
|
.getUnqualifiedType()
|
||||||
|
->getAs<clang::TemplateSpecializationType>();
|
||||||
|
templ != nullptr) {
|
||||||
|
auto *unaliased_type = templ;
|
||||||
|
if (unaliased_type->isTypeAlias())
|
||||||
|
unaliased_type = unaliased_type->getAliasedType()
|
||||||
|
->getAs<clang::TemplateSpecializationType>();
|
||||||
|
|
||||||
|
auto template_specialization_ptr = tbuilder().build(
|
||||||
|
unaliased_type->getTemplateName().getAsTemplateDecl(),
|
||||||
|
*unaliased_type, &c);
|
||||||
|
|
||||||
|
if (diagram().should_include(
|
||||||
|
template_specialization_ptr->full_name(false))) {
|
||||||
|
relationships.emplace_back(
|
||||||
|
template_specialization_ptr->id(), relationship_t::kDependency);
|
||||||
|
|
||||||
|
diagram().add_class(std::move(template_specialization_ptr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
find_relationships(
|
find_relationships(
|
||||||
mf.getReturnType(), relationships, relationship_t::kDependency);
|
mf.getReturnType(), relationships, relationship_t::kDependency);
|
||||||
|
|
||||||
@@ -1317,13 +1341,8 @@ void translation_unit_visitor::process_method(
|
|||||||
if (underlying_type->isPointerType())
|
if (underlying_type->isPointerType())
|
||||||
underlying_type = underlying_type->getPointeeType();
|
underlying_type = underlying_type->getPointeeType();
|
||||||
|
|
||||||
if (const auto *tsp =
|
if (const auto *atsp = underlying_type->getAs<clang::AutoType>();
|
||||||
underlying_type->getAs<clang::TemplateSpecializationType>();
|
atsp != nullptr) {
|
||||||
tsp != nullptr) {
|
|
||||||
process_function_parameter_find_relationships_in_template(c, {}, *tsp);
|
|
||||||
}
|
|
||||||
else if (const auto *atsp = underlying_type->getAs<clang::AutoType>();
|
|
||||||
atsp != nullptr) {
|
|
||||||
process_function_parameter_find_relationships_in_autotype(c, atsp);
|
process_function_parameter_find_relationships_in_autotype(c, atsp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1521,6 +1540,60 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type,
|
|||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (const auto *template_specialization_type =
|
||||||
|
type->getAs<clang::TemplateSpecializationType>();
|
||||||
|
template_specialization_type != nullptr) {
|
||||||
|
if (should_include(template_specialization_type->getTemplateName()
|
||||||
|
.getAsTemplateDecl())) {
|
||||||
|
relationships.emplace_back(
|
||||||
|
template_specialization_type->getTemplateName()
|
||||||
|
.getAsTemplateDecl()
|
||||||
|
->getID(),
|
||||||
|
relationship_hint);
|
||||||
|
}
|
||||||
|
for (const auto &template_argument :
|
||||||
|
template_specialization_type->template_arguments()) {
|
||||||
|
const auto template_argument_kind = template_argument.getKind();
|
||||||
|
if (template_argument_kind ==
|
||||||
|
clang::TemplateArgument::ArgKind::Integral) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
else if (template_argument_kind ==
|
||||||
|
clang::TemplateArgument::ArgKind::Null) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
else if (template_argument_kind ==
|
||||||
|
clang::TemplateArgument::ArgKind::Expression) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
else if (template_argument.getKind() ==
|
||||||
|
clang::TemplateArgument::ArgKind::NullPtr) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
else if (template_argument_kind ==
|
||||||
|
clang::TemplateArgument::ArgKind::Template) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
else if (template_argument_kind ==
|
||||||
|
clang::TemplateArgument::ArgKind::TemplateExpansion) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
else if (const auto *function_type =
|
||||||
|
template_argument.getAsType()
|
||||||
|
->getAs<clang::FunctionProtoType>();
|
||||||
|
function_type != nullptr) {
|
||||||
|
for (const auto ¶m_type : function_type->param_types()) {
|
||||||
|
result = find_relationships(
|
||||||
|
param_type, relationships, relationship_t::kDependency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (template_argument_kind ==
|
||||||
|
clang::TemplateArgument::ArgKind::Type) {
|
||||||
|
result = find_relationships(template_argument.getAsType(),
|
||||||
|
relationships, relationship_hint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -1557,6 +1630,27 @@ void translation_unit_visitor::process_function_parameter(
|
|||||||
// find relationship for the type
|
// find relationship for the type
|
||||||
found_relationships_t relationships;
|
found_relationships_t relationships;
|
||||||
|
|
||||||
|
LOG_DBG("Looking for relationships in type: {}",
|
||||||
|
common::to_string(p.getType(), p.getASTContext()));
|
||||||
|
|
||||||
|
if (const auto *templ =
|
||||||
|
p.getType()
|
||||||
|
.getNonReferenceType()
|
||||||
|
.getUnqualifiedType()
|
||||||
|
->getAs<clang::TemplateSpecializationType>();
|
||||||
|
templ != nullptr) {
|
||||||
|
auto template_specialization_ptr = tbuilder().build(
|
||||||
|
templ->getTemplateName().getAsTemplateDecl(), *templ, &c);
|
||||||
|
|
||||||
|
if (diagram().should_include(
|
||||||
|
template_specialization_ptr->full_name(false))) {
|
||||||
|
relationships.emplace_back(template_specialization_ptr->id(),
|
||||||
|
relationship_t::kDependency);
|
||||||
|
|
||||||
|
diagram().add_class(std::move(template_specialization_ptr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
find_relationships(
|
find_relationships(
|
||||||
p.getType(), relationships, relationship_t::kDependency);
|
p.getType(), relationships, relationship_t::kDependency);
|
||||||
|
|
||||||
@@ -1573,22 +1667,6 @@ void translation_unit_visitor::process_function_parameter(
|
|||||||
c.add_relationship(std::move(r));
|
c.add_relationship(std::move(r));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also consider the container itself if it is a template
|
|
||||||
// instantiation it's arguments could count as reference to relevant
|
|
||||||
// types
|
|
||||||
auto underlying_type = p.getType();
|
|
||||||
if (underlying_type->isReferenceType())
|
|
||||||
underlying_type = underlying_type.getNonReferenceType();
|
|
||||||
if (underlying_type->isPointerType())
|
|
||||||
underlying_type = underlying_type->getPointeeType();
|
|
||||||
|
|
||||||
if (const auto *tsp =
|
|
||||||
underlying_type->getAs<clang::TemplateSpecializationType>();
|
|
||||||
tsp != nullptr) {
|
|
||||||
process_function_parameter_find_relationships_in_template(
|
|
||||||
c, template_parameter_names, *tsp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
method.add_parameter(std::move(parameter));
|
method.add_parameter(std::move(parameter));
|
||||||
@@ -1630,40 +1708,6 @@ void translation_unit_visitor::ensure_lambda_type_is_relative(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void translation_unit_visitor::
|
|
||||||
process_function_parameter_find_relationships_in_template(class_ &c,
|
|
||||||
const std::set<std::string> & /*template_parameter_names*/,
|
|
||||||
const clang::TemplateSpecializationType &template_instantiation_type)
|
|
||||||
{
|
|
||||||
if (!should_include(
|
|
||||||
template_instantiation_type.getTemplateName().getAsTemplateDecl()))
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto template_specialization_ptr = tbuilder().build(
|
|
||||||
template_instantiation_type.getTemplateName().getAsTemplateDecl(),
|
|
||||||
template_instantiation_type, &c);
|
|
||||||
|
|
||||||
if (template_instantiation_type.isDependentType()) {
|
|
||||||
if (template_specialization_ptr) {
|
|
||||||
relationship r{
|
|
||||||
relationship_t::kDependency, template_specialization_ptr->id()};
|
|
||||||
|
|
||||||
c.add_relationship(std::move(r));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (template_specialization_ptr) {
|
|
||||||
relationship r{
|
|
||||||
relationship_t::kDependency, template_specialization_ptr->id()};
|
|
||||||
|
|
||||||
if (!diagram().has_element(template_specialization_ptr->id()))
|
|
||||||
diagram().add_class(std::move(template_specialization_ptr));
|
|
||||||
|
|
||||||
c.add_relationship(std::move(r));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void translation_unit_visitor::add_relationships(class_ &c,
|
void translation_unit_visitor::add_relationships(class_ &c,
|
||||||
const class_member &field, const found_relationships_t &relationships,
|
const class_member &field, const found_relationships_t &relationships,
|
||||||
bool break_on_first_aggregation)
|
bool break_on_first_aggregation)
|
||||||
|
|||||||
@@ -190,11 +190,6 @@ private:
|
|||||||
void process_function_parameter_find_relationships_in_autotype(
|
void process_function_parameter_find_relationships_in_autotype(
|
||||||
model::class_ &c, const clang::AutoType *atsp);
|
model::class_ &c, const clang::AutoType *atsp);
|
||||||
|
|
||||||
void process_function_parameter_find_relationships_in_template(
|
|
||||||
clanguml::class_diagram::model::class_ &c,
|
|
||||||
const std::set<std::string> &template_parameter_names,
|
|
||||||
const clang::TemplateSpecializationType &template_instantiation_type);
|
|
||||||
|
|
||||||
void find_relationships_in_constraint_expression(
|
void find_relationships_in_constraint_expression(
|
||||||
clanguml::common::model::element &c, const clang::Expr *expr);
|
clanguml::common::model::element &c, const clang::Expr *expr);
|
||||||
|
|
||||||
|
|||||||
@@ -161,6 +161,27 @@ std::string to_string(const clang::QualType &type, const clang::ASTContext &ctx,
|
|||||||
clanguml::util::replace_all(result, ", ", ",");
|
clanguml::util::replace_all(result, ", ", ",");
|
||||||
clanguml::util::replace_all(result, "> >", ">>");
|
clanguml::util::replace_all(result, "> >", ">>");
|
||||||
|
|
||||||
|
// Get rid of 'type-parameter-X-Y' ugliness
|
||||||
|
if (result.find("type-parameter-") != std::string::npos) {
|
||||||
|
util::apply_if_not_null(
|
||||||
|
common::dereference(type)->getAs<clang::TypedefType>(),
|
||||||
|
[&result, &type](auto *p) {
|
||||||
|
auto [unqualified_type, context] =
|
||||||
|
common::consume_type_context(type);
|
||||||
|
result = p->getDecl()->getNameAsString();
|
||||||
|
if (!context.empty()) {
|
||||||
|
std::vector<std::string> deduced_contexts;
|
||||||
|
|
||||||
|
for (const auto &c : context) {
|
||||||
|
deduced_contexts.push_back(c.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
result = fmt::format(
|
||||||
|
"{} {}", result, fmt::join(deduced_contexts, " "));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -541,6 +562,97 @@ clang::QualType dereference(clang::QualType type)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<clang::QualType, std::deque<common::model::context>>
|
||||||
|
consume_type_context(clang::QualType type)
|
||||||
|
{
|
||||||
|
std::deque<common::model::context> res;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
bool try_again{false};
|
||||||
|
common::model::context ctx;
|
||||||
|
|
||||||
|
if (type.isConstQualified()) {
|
||||||
|
ctx.is_const = true;
|
||||||
|
try_again = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.isVolatileQualified()) {
|
||||||
|
ctx.is_volatile = true;
|
||||||
|
try_again = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type->isPointerType() || type->isReferenceType()) {
|
||||||
|
if (type.isConstQualified() || type.isVolatileQualified()) {
|
||||||
|
ctx.is_ref_const = type.isConstQualified();
|
||||||
|
ctx.is_ref_volatile = type.isVolatileQualified();
|
||||||
|
|
||||||
|
try_again = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type->isLValueReferenceType()) {
|
||||||
|
ctx.pr = common::model::rpqualifier::kLValueReference;
|
||||||
|
try_again = true;
|
||||||
|
}
|
||||||
|
else if (type->isRValueReferenceType()) {
|
||||||
|
ctx.pr = common::model::rpqualifier::kRValueReference;
|
||||||
|
try_again = true;
|
||||||
|
}
|
||||||
|
else if (type->isMemberFunctionPointerType() &&
|
||||||
|
type->getPointeeType()->getAs<clang::FunctionProtoType>() !=
|
||||||
|
nullptr) {
|
||||||
|
const auto ref_qualifier =
|
||||||
|
type->getPointeeType() // NOLINT
|
||||||
|
->getAs<clang::FunctionProtoType>() // NOLINT
|
||||||
|
->getRefQualifier();
|
||||||
|
|
||||||
|
if (ref_qualifier == clang::RefQualifierKind::RQ_RValue) {
|
||||||
|
ctx.pr = common::model::rpqualifier::kRValueReference;
|
||||||
|
try_again = true;
|
||||||
|
}
|
||||||
|
else if (ref_qualifier == clang::RefQualifierKind::RQ_LValue) {
|
||||||
|
ctx.pr = common::model::rpqualifier::kLValueReference;
|
||||||
|
try_again = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type->isPointerType()) {
|
||||||
|
ctx.pr = common::model::rpqualifier::kPointer;
|
||||||
|
try_again = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (try_again) {
|
||||||
|
if (type->isPointerType()) {
|
||||||
|
if (type->getPointeeType().isConstQualified())
|
||||||
|
ctx.is_const = true;
|
||||||
|
if (type->getPointeeType().isVolatileQualified())
|
||||||
|
ctx.is_volatile = true;
|
||||||
|
|
||||||
|
type = type->getPointeeType().getUnqualifiedType();
|
||||||
|
}
|
||||||
|
else if (type->isReferenceType()) {
|
||||||
|
if (type.getNonReferenceType().isConstQualified())
|
||||||
|
ctx.is_const = true;
|
||||||
|
if (type.getNonReferenceType().isVolatileQualified())
|
||||||
|
ctx.is_volatile = true;
|
||||||
|
|
||||||
|
type = type.getNonReferenceType().getUnqualifiedType();
|
||||||
|
}
|
||||||
|
else if (type.isConstQualified() || type.isVolatileQualified()) {
|
||||||
|
ctx.is_const = type.isConstQualified();
|
||||||
|
ctx.is_volatile = type.isVolatileQualified();
|
||||||
|
type = type.getUnqualifiedType();
|
||||||
|
}
|
||||||
|
|
||||||
|
res.push_front(ctx);
|
||||||
|
|
||||||
|
if (type->isMemberFunctionPointerType())
|
||||||
|
return std::make_pair(type, res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return std::make_pair(type, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> tokenize_unexposed_template_parameter(
|
std::vector<std::string> tokenize_unexposed_template_parameter(
|
||||||
const std::string &t)
|
const std::string &t)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -186,4 +186,7 @@ bool is_type_token(const std::string &t);
|
|||||||
|
|
||||||
clang::QualType dereference(clang::QualType type);
|
clang::QualType dereference(clang::QualType type);
|
||||||
|
|
||||||
|
std::pair<clang::QualType, std::deque<common::model::context>>
|
||||||
|
consume_type_context(clang::QualType type);
|
||||||
|
|
||||||
} // namespace clanguml::common
|
} // namespace clanguml::common
|
||||||
|
|||||||
@@ -634,14 +634,15 @@ void template_parameter::push_context(const context &q)
|
|||||||
{
|
{
|
||||||
context_.push_front(q);
|
context_.push_front(q);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::deque<context> &template_parameter::deduced_context() const
|
const std::deque<context> &template_parameter::deduced_context() const
|
||||||
{
|
{
|
||||||
return context_;
|
return context_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void template_parameter::deduced_context(const std::deque<context> &c)
|
void template_parameter::deduced_context(std::deque<context> c)
|
||||||
{
|
{
|
||||||
context_ = c;
|
context_ = std::move(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
void template_parameter::is_ellipsis(bool e) { is_ellipsis_ = e; }
|
void template_parameter::is_ellipsis(bool e) { is_ellipsis_ = e; }
|
||||||
|
|||||||
@@ -166,8 +166,9 @@ public:
|
|||||||
bool is_array() const;
|
bool is_array() const;
|
||||||
|
|
||||||
void push_context(const context &q);
|
void push_context(const context &q);
|
||||||
|
|
||||||
const std::deque<context> &deduced_context() const;
|
const std::deque<context> &deduced_context() const;
|
||||||
void deduced_context(const std::deque<context> &c);
|
void deduced_context(std::deque<context> c);
|
||||||
|
|
||||||
void is_ellipsis(bool e);
|
void is_ellipsis(bool e);
|
||||||
bool is_ellipsis() const;
|
bool is_ellipsis() const;
|
||||||
|
|||||||
@@ -1,28 +1,53 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
namespace clanguml {
|
namespace clanguml {
|
||||||
namespace t00064 {
|
namespace t00064 {
|
||||||
|
|
||||||
|
// Loosely based on
|
||||||
template <typename... Ts> struct type_list { };
|
template <typename... Ts> struct type_list { };
|
||||||
|
|
||||||
template <typename Ret, typename Arg, typename... Ts>
|
template <typename Ret, typename Arg, typename... Ts>
|
||||||
struct type_list<Ret (*)(Arg &&arg), Ts...> { };
|
struct type_list<Ret (*)(Arg &&), Ts...> { };
|
||||||
|
|
||||||
template <typename T> struct head;
|
template <typename T, typename... Ts> struct type_list<const T, Ts...> { };
|
||||||
|
|
||||||
|
template <typename> struct head;
|
||||||
template <typename Head, typename... Tail>
|
template <typename Head, typename... Tail>
|
||||||
struct head<type_list<Head, Tail...>> {
|
struct head<type_list<Head, Tail...>> {
|
||||||
using type = Head;
|
using type = Head;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T> using head_t = typename head<T>::type;
|
template <typename... Type> using first_t = type_list<Type...>;
|
||||||
|
|
||||||
|
template <typename... Type> using second_t = type_list<Type...>;
|
||||||
|
|
||||||
template <typename, typename> class type_group_pair;
|
template <typename, typename> class type_group_pair;
|
||||||
template <typename... First, typename... Second>
|
template <typename... First, typename... Second>
|
||||||
class type_group_pair<type_list<First...>, type_list<Second...>> {
|
class type_group_pair<first_t<First...>, second_t<Second...>> {
|
||||||
template <typename Type>
|
|
||||||
static constexpr size_t size = sizeof...(First) + sizeof...(Second);
|
static constexpr size_t size = sizeof...(First) + sizeof...(Second);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T> struct optional_ref { };
|
||||||
|
|
||||||
|
template <typename, typename, typename> class type_group_pair_it;
|
||||||
|
template <typename It, typename... First, typename... Second>
|
||||||
|
class type_group_pair_it<It, first_t<First...>, second_t<Second...>> {
|
||||||
|
public:
|
||||||
|
using value_type =
|
||||||
|
decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()),
|
||||||
|
std::declval<First>().get_as_tuple({})...,
|
||||||
|
std::declval<Second>().get_as_tuple({})...));
|
||||||
|
|
||||||
|
using ref_t = optional_ref<value_type>;
|
||||||
|
|
||||||
|
ref_t get(unsigned i) { return {}; }
|
||||||
|
|
||||||
|
const value_type *getp(unsigned i) { return nullptr; }
|
||||||
|
|
||||||
|
constexpr unsigned find(value_type const &v) { return 0; }
|
||||||
|
};
|
||||||
|
|
||||||
struct A { };
|
struct A { };
|
||||||
struct B { };
|
struct B { };
|
||||||
struct C { };
|
struct C { };
|
||||||
|
|||||||
@@ -37,9 +37,37 @@ TEST_CASE("t00064", "[test-case][class]")
|
|||||||
|
|
||||||
REQUIRE_THAT(puml, !Contains("type-parameter-"));
|
REQUIRE_THAT(puml, !Contains("type-parameter-"));
|
||||||
|
|
||||||
// Check if all classes exist
|
REQUIRE_THAT(puml, IsClass(_A("A")));
|
||||||
|
REQUIRE_THAT(puml, IsClass(_A("B")));
|
||||||
|
REQUIRE_THAT(puml, IsClass(_A("C")));
|
||||||
|
REQUIRE_THAT(puml, IsClass(_A("R")));
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml, IsClassTemplate("type_list", "Ts..."));
|
||||||
|
REQUIRE_THAT(puml, IsClassTemplate("type_list", "Ret(Arg &&),Ts..."));
|
||||||
|
REQUIRE_THAT(puml, IsClassTemplate("type_list", "T const,Ts..."));
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml, IsClassTemplate("head", "typename"));
|
||||||
|
REQUIRE_THAT(puml, IsClassTemplate("head", "type_list<Head,Tail...>"));
|
||||||
REQUIRE_THAT(
|
REQUIRE_THAT(
|
||||||
puml, IsClassTemplate("type_group_pair", "typename,typename"));
|
puml, IsClassTemplate("type_group_pair", "typename,typename"));
|
||||||
|
REQUIRE_THAT(puml,
|
||||||
|
IsClassTemplate(
|
||||||
|
"type_group_pair", "type_list<First...>,type_list<Second...>"));
|
||||||
|
REQUIRE_THAT(puml,
|
||||||
|
IsClassTemplate(
|
||||||
|
"type_group_pair", "type_list<float,double>,type_list<A,B,C>"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml, IsClassTemplate("optional_ref", "T"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml,
|
||||||
|
IsClassTemplate("type_group_pair_it",
|
||||||
|
"It,type_list<First...>,type_list<Second...>"));
|
||||||
|
REQUIRE_THAT(
|
||||||
|
puml, (IsMethod<Public>("get", "ref_t", "unsigned int i")));
|
||||||
|
REQUIRE_THAT(puml,
|
||||||
|
(IsMethod<Public>("getp", "value_type const*", "unsigned int i")));
|
||||||
|
REQUIRE_THAT(puml,
|
||||||
|
(IsMethod<Public>("find", "unsigned int", "value_type const& v")));
|
||||||
|
|
||||||
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