Parse cpp_function_template

This commit is contained in:
Jonathan Müller 2017-03-25 14:34:04 +01:00
commit eeb48f1df5
18 changed files with 397 additions and 60 deletions

View file

@ -85,7 +85,7 @@ namespace cppast
/// \requires The comment must not be empty, if there is one. /// \requires The comment must not be empty, if there is one.
void set_comment(type_safe::optional<std::string> comment) noexcept void set_comment(type_safe::optional<std::string> comment) noexcept
{ {
comment_ = std::move(comment.value()); comment_ = std::move(comment.value_or(""));
} }
protected: protected:

View file

@ -64,8 +64,15 @@ namespace cppast
/// \returns Whether or not a given entity kind is a C++ type. /// \returns Whether or not a given entity kind is a C++ type.
bool is_type(cpp_entity_kind kind) noexcept; bool is_type(cpp_entity_kind kind) noexcept;
/// \returns Whether or not a given entity kind is a C++ function.
bool is_function(cpp_entity_kind kind) noexcept;
/// \returns Whether or not a given entity kind is a C++ (template) parameter.
bool is_parameter(cpp_entity_kind kind) noexcept;
/// \returns Whether or not a given entity kind is a C++ template. /// \returns Whether or not a given entity kind is a C++ template.
/// \notes A template template parameter is not considered a template for this function. /// \notes A template template parameter is not considered a template for this function.
/// \notes Template specializations are also considered templates here.
bool is_template(cpp_entity_kind kind) noexcept; bool is_template(cpp_entity_kind kind) noexcept;
} // namespace cppast } // namespace cppast

View file

@ -147,8 +147,9 @@ namespace cppast
/// \returns The finished function without registering it. /// \returns The finished function without registering it.
/// \notes This is intended for templated functions only. /// \notes This is intended for templated functions only.
std::unique_ptr<T> finish() std::unique_ptr<T> finish(cpp_function_body_kind body_kind)
{ {
function->body_ = body_kind;
return std::move(function); return std::move(function);
} }

View file

@ -14,6 +14,8 @@ namespace cppast
class cpp_function_template final : public cpp_template class cpp_function_template final : public cpp_template
{ {
public: public:
static cpp_entity_kind kind() noexcept;
/// Builder for [cppast::cpp_function_template](). /// Builder for [cppast::cpp_function_template]().
class builder : public basic_builder<cpp_function_template, cpp_function_base> class builder : public basic_builder<cpp_function_template, cpp_function_base>
{ {

View file

@ -201,13 +201,7 @@ namespace cppast
class builder : public basic_member_builder<cpp_conversion_op> class builder : public basic_member_builder<cpp_conversion_op>
{ {
public: public:
/// \effects Creates it giving it the return type. using basic_member_builder::basic_member_builder;
/// \notes It does not have a name as it is given by the return type.
builder(std::unique_ptr<cpp_type> type)
{
function =
std::unique_ptr<cpp_conversion_op>(new cpp_conversion_op(std::move(type)));
}
/// \effects Marks the conversion operator `explicit`. /// \effects Marks the conversion operator `explicit`.
void is_explicit() noexcept void is_explicit() noexcept
@ -227,8 +221,8 @@ namespace cppast
} }
private: private:
cpp_conversion_op(std::unique_ptr<cpp_type> return_t) cpp_conversion_op(std::string name, std::unique_ptr<cpp_type> return_t)
: cpp_member_function_base("", std::move(return_t)), explicit_(false) : cpp_member_function_base(std::move(name), std::move(return_t)), explicit_(false)
{ {
} }

View file

@ -135,6 +135,8 @@ namespace cppast
public cpp_variable_base public cpp_variable_base
{ {
public: public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly created and registered non type template parameter. /// \returns A newly created and registered non type template parameter.
/// \notes The `default_value` may be `nullptr` in which case the parameter has no default. /// \notes The `default_value` may be `nullptr` in which case the parameter has no default.
static std::unique_ptr<cpp_non_type_template_parameter> build( static std::unique_ptr<cpp_non_type_template_parameter> build(
@ -173,6 +175,8 @@ namespace cppast
public cpp_entity_container<cpp_template_template_parameter, cpp_template_parameter> public cpp_entity_container<cpp_template_template_parameter, cpp_template_parameter>
{ {
public: public:
static cpp_entity_kind kind() noexcept;
/// Builds a [cppast::cpp_template_template_parameter](). /// Builds a [cppast::cpp_template_template_parameter]().
class builder class builder
{ {

View file

@ -4,12 +4,17 @@
#include <cppast/cpp_entity.hpp> #include <cppast/cpp_entity.hpp>
#include <cppast/cpp_entity_kind.hpp>
using namespace cppast; using namespace cppast;
std::string cppast::full_name(const cpp_entity& e) std::string cppast::full_name(const cpp_entity& e)
{ {
if (e.name().empty()) if (e.name().empty())
return ""; return "";
else if (is_parameter(e.kind()))
// parameters don't have a full name
return e.name();
std::string scopes; std::string scopes;

View file

@ -137,6 +137,95 @@ bool cppast::is_type(cpp_entity_kind kind) noexcept
return false; return false;
} }
bool cppast::is_function(cpp_entity_kind kind) noexcept
{
switch (kind)
{
case cpp_entity_kind::function_t:
case cpp_entity_kind::member_function_t:
case cpp_entity_kind::conversion_op_t:
case cpp_entity_kind::constructor_t:
case cpp_entity_kind::destructor_t:
return true;
case cpp_entity_kind::file_t:
case cpp_entity_kind::macro_definition_t:
case cpp_entity_kind::include_directive_t:
case cpp_entity_kind::language_linkage_t:
case cpp_entity_kind::namespace_t:
case cpp_entity_kind::namespace_alias_t:
case cpp_entity_kind::using_directive_t:
case cpp_entity_kind::using_declaration_t:
case cpp_entity_kind::type_alias_t:
case cpp_entity_kind::enum_t:
case cpp_entity_kind::enum_value_t:
case cpp_entity_kind::class_t:
case cpp_entity_kind::access_specifier_t:
case cpp_entity_kind::base_class_t:
case cpp_entity_kind::variable_t:
case cpp_entity_kind::member_variable_t:
case cpp_entity_kind::bitfield_t:
case cpp_entity_kind::function_parameter_t:
case cpp_entity_kind::template_type_parameter_t:
case cpp_entity_kind::non_type_template_parameter_t:
case cpp_entity_kind::template_template_parameter_t:
case cpp_entity_kind::alias_template_t:
case cpp_entity_kind::variable_template_t:
case cpp_entity_kind::function_template_t:
case cpp_entity_kind::function_template_specialization_t:
case cpp_entity_kind::class_template_t:
case cpp_entity_kind::class_template_specialization_t:
case cpp_entity_kind::count:
break;
}
return false;
}
bool cppast::is_parameter(cpp_entity_kind kind) noexcept
{
switch (kind)
{
case cpp_entity_kind::function_parameter_t:
case cpp_entity_kind::template_type_parameter_t:
case cpp_entity_kind::non_type_template_parameter_t:
case cpp_entity_kind::template_template_parameter_t:
return true;
case cpp_entity_kind::file_t:
case cpp_entity_kind::macro_definition_t:
case cpp_entity_kind::include_directive_t:
case cpp_entity_kind::language_linkage_t:
case cpp_entity_kind::namespace_t:
case cpp_entity_kind::namespace_alias_t:
case cpp_entity_kind::using_directive_t:
case cpp_entity_kind::using_declaration_t:
case cpp_entity_kind::type_alias_t:
case cpp_entity_kind::enum_t:
case cpp_entity_kind::enum_value_t:
case cpp_entity_kind::class_t:
case cpp_entity_kind::access_specifier_t:
case cpp_entity_kind::base_class_t:
case cpp_entity_kind::variable_t:
case cpp_entity_kind::member_variable_t:
case cpp_entity_kind::bitfield_t:
case cpp_entity_kind::function_t:
case cpp_entity_kind::member_function_t:
case cpp_entity_kind::conversion_op_t:
case cpp_entity_kind::constructor_t:
case cpp_entity_kind::destructor_t:
case cpp_entity_kind::alias_template_t:
case cpp_entity_kind::variable_template_t:
case cpp_entity_kind::function_template_t:
case cpp_entity_kind::function_template_specialization_t:
case cpp_entity_kind::class_template_t:
case cpp_entity_kind::class_template_specialization_t:
case cpp_entity_kind::count:
break;
}
return false;
}
bool cppast::is_template(cpp_entity_kind kind) noexcept bool cppast::is_template(cpp_entity_kind kind) noexcept
{ {
switch (kind) switch (kind)

View file

@ -8,11 +8,16 @@
using namespace cppast; using namespace cppast;
cpp_entity_kind cpp_function_template::do_get_entity_kind() const noexcept cpp_entity_kind cpp_function_template::kind() noexcept
{ {
return cpp_entity_kind::function_template_t; return cpp_entity_kind::function_template_t;
} }
cpp_entity_kind cpp_function_template::do_get_entity_kind() const noexcept
{
return kind();
}
cpp_entity_kind cpp_function_template_specialization::do_get_entity_kind() const noexcept cpp_entity_kind cpp_function_template_specialization::do_get_entity_kind() const noexcept
{ {
return cpp_entity_kind::function_template_specialization_t; return cpp_entity_kind::function_template_specialization_t;

View file

@ -57,17 +57,27 @@ std::unique_ptr<cpp_non_type_template_parameter> cpp_non_type_template_parameter
return result; return result;
} }
cpp_entity_kind cpp_non_type_template_parameter::do_get_entity_kind() const noexcept cpp_entity_kind cpp_non_type_template_parameter::kind() noexcept
{ {
return cpp_entity_kind::non_type_template_parameter_t; return cpp_entity_kind::non_type_template_parameter_t;
} }
cpp_entity_kind cpp_non_type_template_parameter::do_get_entity_kind() const noexcept
{
return kind();
}
bool detail::cpp_template_ref_predicate::operator()(const cpp_entity& e) bool detail::cpp_template_ref_predicate::operator()(const cpp_entity& e)
{ {
return is_template(e.kind()) || e.kind() == cpp_entity_kind::template_template_parameter_t; return is_template(e.kind()) || e.kind() == cpp_entity_kind::template_template_parameter_t;
} }
cpp_entity_kind cpp_template_template_parameter::do_get_entity_kind() const noexcept cpp_entity_kind cpp_template_template_parameter::kind() noexcept
{ {
return cpp_entity_kind::template_template_parameter_t; return cpp_entity_kind::template_template_parameter_t;
} }
cpp_entity_kind cpp_template_template_parameter::do_get_entity_kind() const noexcept
{
return kind();
}

View file

@ -20,8 +20,10 @@ namespace
std::unique_ptr<cpp_expression> default_value; std::unique_ptr<cpp_expression> default_value;
detail::visit_children(cur, [&](const CXCursor& child) { detail::visit_children(cur, [&](const CXCursor& child) {
DEBUG_ASSERT(clang_isExpression(child.kind) && !default_value, if (!clang_isExpression(clang_getCursorKind(child)))
detail::parse_error_handler{}, child, return;
DEBUG_ASSERT(!default_value, detail::parse_error_handler{}, child,
"unexpected child cursor of function parameter"); "unexpected child cursor of function parameter");
default_value = detail::parse_expression(context, child); default_value = detail::parse_expression(context, child);
}); });
@ -281,6 +283,7 @@ namespace
detail::parse_type(context, cur, detail::parse_type(context, cur,
clang_getCursorResultType(cur))); clang_getCursorResultType(cur)));
context.comments.match(builder.get(), cur); context.comments.match(builder.get(), cur);
add_parameters(context, builder, cur); add_parameters(context, builder, cur);
if (clang_Cursor_isVariadic(cur)) if (clang_Cursor_isVariadic(cur))
builder.is_variadic(); builder.is_variadic();
@ -301,21 +304,28 @@ namespace
if (suffix.noexcept_condition) if (suffix.noexcept_condition)
builder.noexcept_condition(std::move(suffix.noexcept_condition)); builder.noexcept_condition(std::move(suffix.noexcept_condition));
return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind); if (clang_getTemplateCursorKind(cur) == CXCursor_NoDeclFound)
return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind);
else
return builder.finish(suffix.body_kind);
} }
} }
std::unique_ptr<cpp_entity> detail::parse_cpp_function(const detail::parse_context& context, std::unique_ptr<cpp_entity> detail::parse_cpp_function(const detail::parse_context& context,
const CXCursor& cur) const CXCursor& cur)
{ {
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_FunctionDecl, detail::assert_handler{}); DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_FunctionDecl
|| clang_getTemplateCursorKind(cur) == CXCursor_FunctionDecl,
detail::assert_handler{});
return parse_cpp_function_impl(context, cur); return parse_cpp_function_impl(context, cur);
} }
std::unique_ptr<cpp_entity> detail::try_parse_static_cpp_function( std::unique_ptr<cpp_entity> detail::try_parse_static_cpp_function(
const detail::parse_context& context, const CXCursor& cur) const detail::parse_context& context, const CXCursor& cur)
{ {
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_CXXMethod, detail::assert_handler{}); DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_CXXMethod
|| clang_getTemplateCursorKind(cur) == CXCursor_CXXMethod,
detail::assert_handler{});
if (clang_CXXMethod_isStatic(cur)) if (clang_CXXMethod_isStatic(cur))
return parse_cpp_function_impl(context, cur); return parse_cpp_function_impl(context, cur);
return nullptr; return nullptr;
@ -410,7 +420,9 @@ namespace
std::unique_ptr<cpp_entity> detail::parse_cpp_member_function(const detail::parse_context& context, std::unique_ptr<cpp_entity> detail::parse_cpp_member_function(const detail::parse_context& context,
const CXCursor& cur) const CXCursor& cur)
{ {
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_CXXMethod, detail::assert_handler{}); DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_CXXMethod
|| clang_getTemplateCursorKind(cur) == CXCursor_CXXMethod,
detail::assert_handler{});
auto name = detail::get_cursor_name(cur); auto name = detail::get_cursor_name(cur);
cpp_member_function::builder builder(name.c_str(), cpp_member_function::builder builder(name.c_str(),
@ -435,9 +447,14 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_member_function(const detail::pars
std::unique_ptr<cpp_entity> detail::parse_cpp_conversion_op(const detail::parse_context& context, std::unique_ptr<cpp_entity> detail::parse_cpp_conversion_op(const detail::parse_context& context,
const CXCursor& cur) const CXCursor& cur)
{ {
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_ConversionFunction, detail::assert_handler{}); DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_ConversionFunction
cpp_conversion_op::builder builder( || clang_getTemplateCursorKind(cur) == CXCursor_ConversionFunction,
detail::parse_type(context, cur, clang_getCursorResultType(cur))); detail::assert_handler{});
auto type = clang_getCursorResultType(cur);
cpp_conversion_op::builder builder(std::string("operator ")
+ cxstring(clang_getTypeSpelling(type)).c_str(),
detail::parse_type(context, cur, type));
context.comments.match(builder.get(), cur); context.comments.match(builder.get(), cur);
detail::tokenizer tokenizer(context.tu, context.file, cur); detail::tokenizer tokenizer(context.tu, context.file, cur);
@ -455,7 +472,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_conversion_op(const detail::parse_
else if (detail::skip_if(stream, "explicit")) else if (detail::skip_if(stream, "explicit"))
builder.is_explicit(); builder.is_explicit();
else else
DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, "unexpected token"); stream.bump();
} }
// heuristic to find arguments tokens // heuristic to find arguments tokens
@ -485,7 +502,9 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_conversion_op(const detail::parse_
std::unique_ptr<cpp_entity> detail::parse_cpp_constructor(const detail::parse_context& context, std::unique_ptr<cpp_entity> detail::parse_cpp_constructor(const detail::parse_context& context,
const CXCursor& cur) const CXCursor& cur)
{ {
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_Constructor, detail::assert_handler{}); DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_Constructor
|| clang_getTemplateCursorKind(cur) == CXCursor_Constructor,
detail::assert_handler{});
auto name = detail::get_cursor_name(cur); auto name = detail::get_cursor_name(cur);
cpp_constructor::builder builder(name.c_str()); cpp_constructor::builder builder(name.c_str());
@ -505,7 +524,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_constructor(const detail::parse_co
else if (detail::skip_if(stream, "explicit")) else if (detail::skip_if(stream, "explicit"))
builder.is_explicit(); builder.is_explicit();
else else
DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, "unexpected token"); stream.bump();
} }
skip_parameters(stream); skip_parameters(stream);

View file

@ -22,6 +22,9 @@ detail::cxstring detail::get_cursor_name(const CXCursor& cur)
cpp_storage_class_specifiers detail::get_storage_class(const CXCursor& cur) cpp_storage_class_specifiers detail::get_storage_class(const CXCursor& cur)
{ {
if (clang_getTemplateCursorKind(cur) != CXCursor_NoDeclFound)
return cpp_storage_class_none;
switch (clang_Cursor_getStorageClass(cur)) switch (clang_Cursor_getStorageClass(cur))
{ {
case CX_SC_Invalid: case CX_SC_Invalid:
@ -42,11 +45,11 @@ cpp_storage_class_specifiers detail::get_storage_class(const CXCursor& cur)
case CX_SC_PrivateExtern: case CX_SC_PrivateExtern:
case CX_SC_OpenCLWorkGroupLocal: case CX_SC_OpenCLWorkGroupLocal:
// non-exposed storage classes // non-exposed storage classes
return cpp_storage_class_auto; return cpp_storage_class_none;
} }
DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, "unexpected storage class"); DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, "unexpected storage class");
return cpp_storage_class_auto; return cpp_storage_class_none;
} }
void detail::comment_context::match(cpp_entity& e, const CXCursor& cur) const void detail::comment_context::match(cpp_entity& e, const CXCursor& cur) const
@ -120,6 +123,8 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
case CXCursor_TypeAliasTemplateDecl: case CXCursor_TypeAliasTemplateDecl:
return parse_cpp_alias_template(context, cur); return parse_cpp_alias_template(context, cur);
case CXCursor_FunctionTemplate:
return parse_cpp_function_template(context, cur);
default: default:
break; break;

View file

@ -127,6 +127,8 @@ namespace cppast
std::unique_ptr<cpp_entity> parse_cpp_alias_template(const parse_context& context, std::unique_ptr<cpp_entity> parse_cpp_alias_template(const parse_context& context,
const CXCursor& cur); const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_function_template(const parse_context& context,
const CXCursor& cur);
// as_template: true, iff currently parsing a template // as_template: true, iff currently parsing a template
std::unique_ptr<cpp_entity> parse_entity( std::unique_ptr<cpp_entity> parse_entity(

View file

@ -3,6 +3,9 @@
// found in the top-level directory of this distribution. // found in the top-level directory of this distribution.
#include <cppast/cpp_alias_template.hpp> #include <cppast/cpp_alias_template.hpp>
#include <cppast/cpp_function_template.hpp>
#include <cppast/cpp_entity_kind.hpp>
#include "libclang_visitor.hpp" #include "libclang_visitor.hpp"
#include "parse_functions.hpp" #include "parse_functions.hpp"
@ -11,9 +14,9 @@ using namespace cppast;
namespace namespace
{ {
template <typename TemplateT, typename EntityT> template <typename TemplateT, typename EntityT, typename Predicate>
type_safe::optional<typename TemplateT::builder> get_builder( type_safe::optional<typename TemplateT::builder> get_builder(
const detail::parse_context& context, const CXCursor& cur) const detail::parse_context& context, const CXCursor& cur, Predicate p)
{ {
// we need the actual entity first, then the parameters // we need the actual entity first, then the parameters
// so two visit calls are required // so two visit calls are required
@ -34,7 +37,7 @@ namespace
auto entity = detail::parse_entity(context, result, cur); auto entity = detail::parse_entity(context, result, cur);
if (!entity) if (!entity)
return type_safe::nullopt; return type_safe::nullopt;
DEBUG_ASSERT(entity->kind() == EntityT::kind(), detail::parse_error_handler{}, cur, DEBUG_ASSERT(p(entity->kind()), detail::parse_error_handler{}, cur,
"wrong child of template"); "wrong child of template");
return typename TemplateT::builder( return typename TemplateT::builder(
std::unique_ptr<EntityT>(static_cast<EntityT*>(entity.release()))); std::unique_ptr<EntityT>(static_cast<EntityT*>(entity.release())));
@ -206,10 +209,55 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_alias_template(const detail::parse
{ {
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TypeAliasTemplateDecl, DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TypeAliasTemplateDecl,
detail::assert_handler{}); detail::assert_handler{});
auto builder = get_builder<cpp_alias_template, cpp_type_alias>(context, cur); auto builder =
get_builder<cpp_alias_template, cpp_type_alias>(context, cur, [](cpp_entity_kind k) {
return k == cpp_entity_kind::type_alias_t;
});
if (!builder) if (!builder)
return nullptr; return nullptr;
context.comments.match(builder.value().get(), cur); context.comments.match(builder.value().get(), cur);
parse_parameters(builder.value(), context, cur); parse_parameters(builder.value(), context, cur);
return builder.value().finish(*context.idx, detail::get_entity_id(cur)); return builder.value().finish(*context.idx, detail::get_entity_id(cur));
} }
std::unique_ptr<cpp_entity> detail::parse_cpp_function_template(
const detail::parse_context& context, const CXCursor& cur)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_FunctionTemplate, detail::assert_handler{});
std::unique_ptr<cpp_entity> func;
switch (clang_getTemplateCursorKind(cur))
{
case CXCursor_FunctionDecl:
func = detail::parse_cpp_function(context, cur);
break;
case CXCursor_CXXMethod:
if (auto sfunc = detail::try_parse_static_cpp_function(context, cur))
func = std::move(sfunc);
else
func = detail::parse_cpp_member_function(context, cur);
break;
case CXCursor_ConversionFunction:
func = detail::parse_cpp_conversion_op(context, cur);
break;
case CXCursor_Constructor:
func = detail::parse_cpp_constructor(context, cur);
break;
default:
DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, "unexpected template cursor kind");
}
if (!func)
return nullptr;
// steal comment
auto comment = type_safe::copy(func->comment());
func->set_comment(type_safe::nullopt);
cpp_function_template::builder builder(
std::unique_ptr<cpp_function_base>(static_cast<cpp_function_base*>(func.release())));
builder.get().set_comment(std::move(comment));
parse_parameters(builder, context, cur);
return builder.finish(*context.idx, detail::get_entity_id(cur));
}

View file

@ -37,41 +37,26 @@ template <typename T>
using h = g<T, a>; using h = g<T, a>;
)"; )";
auto check_parameters =
[](const cpp_alias_template& alias,
std::initializer_list<std::pair<cpp_entity_kind, const char*>> params) {
// no need to check more
auto cur = params.begin();
for (auto& param : alias.parameters())
{
REQUIRE(cur != params.end());
REQUIRE(param.kind() == cur->first);
REQUIRE(param.name() == cur->second);
++cur;
}
REQUIRE(cur == params.end());
};
cpp_entity_index idx; cpp_entity_index idx;
auto file = parse(idx, "cpp_alias_template.cpp", code); auto file = parse(idx, "cpp_alias_template.cpp", code);
auto count = test_visit<cpp_alias_template>(*file, [&](const cpp_alias_template& alias) { auto count = test_visit<cpp_alias_template>(*file, [&](const cpp_alias_template& alias) {
if (alias.name() == "a") if (alias.name() == "a")
{ {
check_parameters(alias, {{cpp_entity_kind::template_type_parameter_t, "T"}}); check_template_parameters(alias, {{cpp_entity_kind::template_type_parameter_t, "T"}});
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(), REQUIRE(equal_types(idx, alias.type_alias().underlying_type(),
*cpp_builtin_type::build("int"))); *cpp_builtin_type::build("int")));
} }
else if (alias.name() == "b") else if (alias.name() == "b")
{ {
check_parameters(alias, {{cpp_entity_kind::non_type_template_parameter_t, "I"}, check_template_parameters(alias, {{cpp_entity_kind::non_type_template_parameter_t, "I"},
{cpp_entity_kind::template_type_parameter_t, "T"}}); {cpp_entity_kind::template_type_parameter_t, "T"}});
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(), REQUIRE(equal_types(idx, alias.type_alias().underlying_type(),
*cpp_template_parameter_type::build( *cpp_template_parameter_type::build(
cpp_template_type_parameter_ref(cpp_entity_id(""), "T")))); cpp_template_type_parameter_ref(cpp_entity_id(""), "T"))));
} }
else if (alias.name() == "c") else if (alias.name() == "c")
{ {
check_parameters(alias, {{cpp_entity_kind::template_type_parameter_t, "T"}}); check_template_parameters(alias, {{cpp_entity_kind::template_type_parameter_t, "T"}});
auto param = cpp_template_parameter_type::build( auto param = cpp_template_parameter_type::build(
cpp_template_type_parameter_ref(cpp_entity_id(""), "T")); cpp_template_type_parameter_ref(cpp_entity_id(""), "T"));
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(), REQUIRE(equal_types(idx, alias.type_alias().underlying_type(),
@ -80,7 +65,7 @@ using h = g<T, a>;
} }
else if (alias.name() == "d") else if (alias.name() == "d")
{ {
check_parameters(alias, {{cpp_entity_kind::template_type_parameter_t, "T"}}); check_template_parameters(alias, {{cpp_entity_kind::template_type_parameter_t, "T"}});
cpp_template_instantiation_type::builder builder( cpp_template_instantiation_type::builder builder(
cpp_template_ref(cpp_entity_id(""), "a")); cpp_template_ref(cpp_entity_id(""), "a"));
@ -89,7 +74,8 @@ using h = g<T, a>;
} }
else if (alias.name() == "e") else if (alias.name() == "e")
{ {
check_parameters(alias, {{cpp_entity_kind::non_type_template_parameter_t, "I"}}); check_template_parameters(alias,
{{cpp_entity_kind::non_type_template_parameter_t, "I"}});
cpp_template_instantiation_type::builder builder( cpp_template_instantiation_type::builder builder(
cpp_template_ref(cpp_entity_id(""), "b")); cpp_template_ref(cpp_entity_id(""), "b"));
@ -100,7 +86,8 @@ using h = g<T, a>;
} }
else if (alias.name() == "f") else if (alias.name() == "f")
{ {
check_parameters(alias, {{cpp_entity_kind::non_type_template_parameter_t, "I"}}); check_template_parameters(alias,
{{cpp_entity_kind::non_type_template_parameter_t, "I"}});
cpp_template_instantiation_type::builder builder( cpp_template_instantiation_type::builder builder(
cpp_template_ref(cpp_entity_id(""), "b")); cpp_template_ref(cpp_entity_id(""), "b"));
@ -111,8 +98,9 @@ using h = g<T, a>;
} }
else if (alias.name() == "g") else if (alias.name() == "g")
{ {
check_parameters(alias, {{cpp_entity_kind::template_type_parameter_t, "T"}, check_template_parameters(alias,
{cpp_entity_kind::template_template_parameter_t, "Templ"}}); {{cpp_entity_kind::template_type_parameter_t, "T"},
{cpp_entity_kind::template_template_parameter_t, "Templ"}});
cpp_template_instantiation_type::builder builder( cpp_template_instantiation_type::builder builder(
cpp_template_ref(cpp_entity_id(""), "Templ")); cpp_template_ref(cpp_entity_id(""), "Templ"));
@ -121,7 +109,7 @@ using h = g<T, a>;
} }
else if (alias.name() == "h") else if (alias.name() == "h")
{ {
check_parameters(alias, {{cpp_entity_kind::template_type_parameter_t, "T"}}); check_template_parameters(alias, {{cpp_entity_kind::template_type_parameter_t, "T"}});
cpp_template_instantiation_type::builder builder( cpp_template_instantiation_type::builder builder(
cpp_template_ref(cpp_entity_id(""), "g")); cpp_template_ref(cpp_entity_id(""), "g"));

View file

@ -0,0 +1,142 @@
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
// This file is subject to the license terms in the LICENSE file
// found in the top-level directory of this distribution.
#include <cppast/cpp_function_template.hpp>
#include <cppast/cpp_member_function.hpp>
#include "test_parser.hpp"
using namespace cppast;
TEST_CASE("cpp_function_template")
{
// only check templated related stuff
auto code = R"(
template <typename T>
T a(const T& t);
template <int I>
using type = int;
struct d
{
template <int I, typename T>
static type<I> b(T);
template <typename T = const int>
auto c() -> T;
template <typename T>
operator T() const;
template <typename T>
d(const T&);
};
)";
cpp_entity_index idx;
auto file = parse(idx, "cpp_function_template.cpp", code);
auto count = test_visit<cpp_function_template>(*file, [&](const cpp_function_template& tfunc) {
if (tfunc.name() == "a")
{
check_template_parameters(tfunc, {{cpp_entity_kind::template_type_parameter_t, "T"}});
REQUIRE(tfunc.function().kind() == cpp_entity_kind::function_t);
auto& func = static_cast<const cpp_function&>(tfunc.function());
auto parameter = cpp_template_type_parameter_ref(cpp_entity_id(""), "T");
REQUIRE(equal_types(idx, func.return_type(),
*cpp_template_parameter_type::build(parameter)));
auto count = 0u;
for (auto& param : func)
{
++count;
REQUIRE(
equal_types(idx, param.type(),
*cpp_reference_type::
build(cpp_cv_qualified_type::
build(cpp_template_parameter_type::build(parameter),
cpp_cv_const),
cpp_ref_lvalue)));
}
REQUIRE(count == 1u);
}
else if (tfunc.name() == "b")
{
check_parent(tfunc, "d", "d::b");
check_template_parameters(tfunc, {{cpp_entity_kind::non_type_template_parameter_t, "I"},
{cpp_entity_kind::template_type_parameter_t, "T"}});
REQUIRE(tfunc.function().kind() == cpp_entity_kind::function_t);
auto& func = static_cast<const cpp_function&>(tfunc.function());
cpp_template_instantiation_type::builder builder(
cpp_template_ref(cpp_entity_id(""), "type"));
builder.add_argument(
cpp_unexposed_expression::build(cpp_builtin_type::build("int"), "I"));
REQUIRE(equal_types(idx, func.return_type(), *builder.finish()));
auto type_parameter = cpp_template_type_parameter_ref(cpp_entity_id(""), "T");
auto count = 0u;
for (auto& param : func)
{
++count;
REQUIRE(equal_types(idx, param.type(),
*cpp_template_parameter_type::build(type_parameter)));
}
REQUIRE(count == 1u);
}
else if (tfunc.name() == "c")
{
check_template_parameters(tfunc, {{cpp_entity_kind::template_type_parameter_t, "T"}});
REQUIRE(tfunc.function().kind() == cpp_entity_kind::member_function_t);
auto& func = static_cast<const cpp_member_function&>(tfunc.function());
REQUIRE(func.cv_qualifier() == cpp_cv_none);
auto parameter = cpp_template_type_parameter_ref(cpp_entity_id(""), "T");
REQUIRE(equal_types(idx, func.return_type(),
*cpp_template_parameter_type::build(parameter)));
}
else if (tfunc.name() == "operator T")
{
check_template_parameters(tfunc, {{cpp_entity_kind::template_type_parameter_t, "T"}});
REQUIRE(tfunc.function().kind() == cpp_entity_kind::conversion_op_t);
auto& func = static_cast<const cpp_conversion_op&>(tfunc.function());
REQUIRE(func.cv_qualifier() == cpp_cv_const);
auto parameter = cpp_template_type_parameter_ref(cpp_entity_id(""), "T");
REQUIRE(equal_types(idx, func.return_type(),
*cpp_template_parameter_type::build(parameter)));
}
else if (tfunc.name() == "d")
{
check_template_parameters(tfunc, {{cpp_entity_kind::template_type_parameter_t, "T"}});
REQUIRE(tfunc.function().kind() == cpp_entity_kind::constructor_t);
auto& func = static_cast<const cpp_constructor&>(tfunc.function());
auto parameter = cpp_template_type_parameter_ref(cpp_entity_id(""), "T");
auto count = 0u;
for (auto& param : func)
{
++count;
REQUIRE(
equal_types(idx, param.type(),
*cpp_reference_type::
build(cpp_cv_qualified_type::
build(cpp_template_parameter_type::build(parameter),
cpp_cv_const),
cpp_ref_lvalue)));
}
REQUIRE(count == 1u);
}
else
REQUIRE(false);
});
REQUIRE(count == 5u);
}

View file

@ -151,7 +151,6 @@ struct foo
cpp_entity_index idx; cpp_entity_index idx;
auto file = parse(idx, "cpp_conversion_op.cpp", code); auto file = parse(idx, "cpp_conversion_op.cpp", code);
auto count = test_visit<cpp_conversion_op>(*file, [&](const cpp_conversion_op& op) { auto count = test_visit<cpp_conversion_op>(*file, [&](const cpp_conversion_op& op) {
REQUIRE(op.name().empty());
REQUIRE(count_children(op) == 0u); REQUIRE(count_children(op) == 0u);
REQUIRE(!op.is_variadic()); REQUIRE(!op.is_variadic());
REQUIRE(op.body_kind() == cpp_function_declaration); REQUIRE(op.body_kind() == cpp_function_declaration);
@ -161,20 +160,21 @@ struct foo
if (!op.is_explicit() && !op.is_constexpr()) if (!op.is_explicit() && !op.is_constexpr())
{ {
REQUIRE(op.name() == "operator int &");
REQUIRE(equal_types(idx, op.return_type(), REQUIRE(equal_types(idx, op.return_type(),
*cpp_reference_type::build(cpp_builtin_type::build("int"), *cpp_reference_type::build(cpp_builtin_type::build("int"),
cpp_ref_lvalue))); cpp_ref_lvalue)));
REQUIRE(op.cv_qualifier() == cpp_cv_none); REQUIRE(op.cv_qualifier() == cpp_cv_none);
REQUIRE(!op.is_explicit());
REQUIRE(!op.is_constexpr());
} }
else if (op.is_explicit() && !op.is_constexpr()) else if (op.is_explicit() && !op.is_constexpr())
{ {
REQUIRE(op.name() == "operator bool");
REQUIRE(equal_types(idx, op.return_type(), *cpp_builtin_type::build("bool"))); REQUIRE(equal_types(idx, op.return_type(), *cpp_builtin_type::build("bool")));
REQUIRE(op.cv_qualifier() == cpp_cv_const); REQUIRE(op.cv_qualifier() == cpp_cv_const);
} }
else if (!op.is_explicit() && op.is_constexpr()) else if (!op.is_explicit() && op.is_constexpr())
{ {
REQUIRE(op.name() == "operator ns::type");
REQUIRE(equal_types(idx, op.return_type(), REQUIRE(equal_types(idx, op.return_type(),
*cpp_user_defined_type::build( *cpp_user_defined_type::build(
cpp_type_ref(cpp_entity_id(""), "ns::type")))); cpp_type_ref(cpp_entity_id(""), "ns::type"))));

View file

@ -100,4 +100,20 @@ inline bool equal_expressions(const cppast::cpp_expression& parsed,
return false; return false;
} }
template <typename T>
void check_template_parameters(
const T& templ, std::initializer_list<std::pair<cppast::cpp_entity_kind, const char*>> params)
{
// no need to check more
auto cur = params.begin();
for (auto& param : templ.parameters())
{
REQUIRE(cur != params.end());
REQUIRE(param.kind() == cur->first);
REQUIRE(param.name() == cur->second);
++cur;
}
REQUIRE(cur == params.end());
}
#endif // CPPAST_TEST_PARSER_HPP_INCLUDED #endif // CPPAST_TEST_PARSER_HPP_INCLUDED