diff --git a/include/cppast/cpp_entity.hpp b/include/cppast/cpp_entity.hpp index cf760e0..4512f03 100644 --- a/include/cppast/cpp_entity.hpp +++ b/include/cppast/cpp_entity.hpp @@ -85,7 +85,7 @@ namespace cppast /// \requires The comment must not be empty, if there is one. void set_comment(type_safe::optional comment) noexcept { - comment_ = std::move(comment.value()); + comment_ = std::move(comment.value_or("")); } protected: diff --git a/include/cppast/cpp_entity_kind.hpp b/include/cppast/cpp_entity_kind.hpp index 4403c28..3a3a3f8 100644 --- a/include/cppast/cpp_entity_kind.hpp +++ b/include/cppast/cpp_entity_kind.hpp @@ -64,8 +64,15 @@ namespace cppast /// \returns Whether or not a given entity kind is a C++ type. 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. /// \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; } // namespace cppast diff --git a/include/cppast/cpp_function.hpp b/include/cppast/cpp_function.hpp index 6dc06e0..d161fa1 100644 --- a/include/cppast/cpp_function.hpp +++ b/include/cppast/cpp_function.hpp @@ -147,8 +147,9 @@ namespace cppast /// \returns The finished function without registering it. /// \notes This is intended for templated functions only. - std::unique_ptr finish() + std::unique_ptr finish(cpp_function_body_kind body_kind) { + function->body_ = body_kind; return std::move(function); } diff --git a/include/cppast/cpp_function_template.hpp b/include/cppast/cpp_function_template.hpp index 167e727..d5d536d 100644 --- a/include/cppast/cpp_function_template.hpp +++ b/include/cppast/cpp_function_template.hpp @@ -14,6 +14,8 @@ namespace cppast class cpp_function_template final : public cpp_template { public: + static cpp_entity_kind kind() noexcept; + /// Builder for [cppast::cpp_function_template](). class builder : public basic_builder { diff --git a/include/cppast/cpp_member_function.hpp b/include/cppast/cpp_member_function.hpp index 515d54a..c6dec70 100644 --- a/include/cppast/cpp_member_function.hpp +++ b/include/cppast/cpp_member_function.hpp @@ -201,13 +201,7 @@ namespace cppast class builder : public basic_member_builder { public: - /// \effects Creates it giving it the return type. - /// \notes It does not have a name as it is given by the return type. - builder(std::unique_ptr type) - { - function = - std::unique_ptr(new cpp_conversion_op(std::move(type))); - } + using basic_member_builder::basic_member_builder; /// \effects Marks the conversion operator `explicit`. void is_explicit() noexcept @@ -227,8 +221,8 @@ namespace cppast } private: - cpp_conversion_op(std::unique_ptr return_t) - : cpp_member_function_base("", std::move(return_t)), explicit_(false) + cpp_conversion_op(std::string name, std::unique_ptr return_t) + : cpp_member_function_base(std::move(name), std::move(return_t)), explicit_(false) { } diff --git a/include/cppast/cpp_template_parameter.hpp b/include/cppast/cpp_template_parameter.hpp index 7c92c1c..dae27e1 100644 --- a/include/cppast/cpp_template_parameter.hpp +++ b/include/cppast/cpp_template_parameter.hpp @@ -135,6 +135,8 @@ namespace cppast public cpp_variable_base { public: + static cpp_entity_kind kind() noexcept; + /// \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. static std::unique_ptr build( @@ -173,6 +175,8 @@ namespace cppast public cpp_entity_container { public: + static cpp_entity_kind kind() noexcept; + /// Builds a [cppast::cpp_template_template_parameter](). class builder { diff --git a/src/cpp_entity.cpp b/src/cpp_entity.cpp index 3c370c5..82dc485 100644 --- a/src/cpp_entity.cpp +++ b/src/cpp_entity.cpp @@ -4,12 +4,17 @@ #include +#include + using namespace cppast; std::string cppast::full_name(const cpp_entity& e) { if (e.name().empty()) return ""; + else if (is_parameter(e.kind())) + // parameters don't have a full name + return e.name(); std::string scopes; diff --git a/src/cpp_entity_kind.cpp b/src/cpp_entity_kind.cpp index 07414f2..cfd6ea9 100644 --- a/src/cpp_entity_kind.cpp +++ b/src/cpp_entity_kind.cpp @@ -137,6 +137,95 @@ bool cppast::is_type(cpp_entity_kind kind) noexcept 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 { switch (kind) diff --git a/src/cpp_function_template.cpp b/src/cpp_function_template.cpp index 64f262e..a4e6101 100644 --- a/src/cpp_function_template.cpp +++ b/src/cpp_function_template.cpp @@ -8,11 +8,16 @@ 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; } +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 { return cpp_entity_kind::function_template_specialization_t; diff --git a/src/cpp_template_parameter.cpp b/src/cpp_template_parameter.cpp index 7c658f1..61122ad 100644 --- a/src/cpp_template_parameter.cpp +++ b/src/cpp_template_parameter.cpp @@ -57,17 +57,27 @@ std::unique_ptr cpp_non_type_template_parameter 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; } +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) { 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; } + +cpp_entity_kind cpp_template_template_parameter::do_get_entity_kind() const noexcept +{ + return kind(); +} diff --git a/src/libclang/function_parser.cpp b/src/libclang/function_parser.cpp index d41c774..0a92aed 100644 --- a/src/libclang/function_parser.cpp +++ b/src/libclang/function_parser.cpp @@ -20,8 +20,10 @@ namespace std::unique_ptr default_value; detail::visit_children(cur, [&](const CXCursor& child) { - DEBUG_ASSERT(clang_isExpression(child.kind) && !default_value, - detail::parse_error_handler{}, child, + if (!clang_isExpression(clang_getCursorKind(child))) + return; + + DEBUG_ASSERT(!default_value, detail::parse_error_handler{}, child, "unexpected child cursor of function parameter"); default_value = detail::parse_expression(context, child); }); @@ -281,6 +283,7 @@ namespace detail::parse_type(context, cur, clang_getCursorResultType(cur))); context.comments.match(builder.get(), cur); + add_parameters(context, builder, cur); if (clang_Cursor_isVariadic(cur)) builder.is_variadic(); @@ -301,21 +304,28 @@ namespace if (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 detail::parse_cpp_function(const detail::parse_context& context, 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); } std::unique_ptr detail::try_parse_static_cpp_function( 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)) return parse_cpp_function_impl(context, cur); return nullptr; @@ -410,7 +420,9 @@ namespace std::unique_ptr detail::parse_cpp_member_function(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{}); auto name = detail::get_cursor_name(cur); cpp_member_function::builder builder(name.c_str(), @@ -435,9 +447,14 @@ std::unique_ptr detail::parse_cpp_member_function(const detail::pars std::unique_ptr detail::parse_cpp_conversion_op(const detail::parse_context& context, const CXCursor& cur) { - DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_ConversionFunction, detail::assert_handler{}); - cpp_conversion_op::builder builder( - detail::parse_type(context, cur, clang_getCursorResultType(cur))); + DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_ConversionFunction + || clang_getTemplateCursorKind(cur) == CXCursor_ConversionFunction, + 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); detail::tokenizer tokenizer(context.tu, context.file, cur); @@ -455,7 +472,7 @@ std::unique_ptr detail::parse_cpp_conversion_op(const detail::parse_ else if (detail::skip_if(stream, "explicit")) builder.is_explicit(); else - DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, "unexpected token"); + stream.bump(); } // heuristic to find arguments tokens @@ -485,7 +502,9 @@ std::unique_ptr detail::parse_cpp_conversion_op(const detail::parse_ std::unique_ptr detail::parse_cpp_constructor(const detail::parse_context& context, 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); cpp_constructor::builder builder(name.c_str()); @@ -505,7 +524,7 @@ std::unique_ptr detail::parse_cpp_constructor(const detail::parse_co else if (detail::skip_if(stream, "explicit")) builder.is_explicit(); else - DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, "unexpected token"); + stream.bump(); } skip_parameters(stream); diff --git a/src/libclang/parse_functions.cpp b/src/libclang/parse_functions.cpp index e2754d7..6fba89b 100644 --- a/src/libclang/parse_functions.cpp +++ b/src/libclang/parse_functions.cpp @@ -22,6 +22,9 @@ detail::cxstring detail::get_cursor_name(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)) { 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_OpenCLWorkGroupLocal: // non-exposed storage classes - return cpp_storage_class_auto; + return cpp_storage_class_none; } 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 @@ -120,6 +123,8 @@ std::unique_ptr detail::parse_entity(const detail::parse_context& co case CXCursor_TypeAliasTemplateDecl: return parse_cpp_alias_template(context, cur); + case CXCursor_FunctionTemplate: + return parse_cpp_function_template(context, cur); default: break; diff --git a/src/libclang/parse_functions.hpp b/src/libclang/parse_functions.hpp index 6d847c4..7785cde 100644 --- a/src/libclang/parse_functions.hpp +++ b/src/libclang/parse_functions.hpp @@ -127,6 +127,8 @@ namespace cppast std::unique_ptr parse_cpp_alias_template(const parse_context& context, const CXCursor& cur); + std::unique_ptr parse_cpp_function_template(const parse_context& context, + const CXCursor& cur); // as_template: true, iff currently parsing a template std::unique_ptr parse_entity( diff --git a/src/libclang/template_parser.cpp b/src/libclang/template_parser.cpp index 3327a3c..3e2e05b 100644 --- a/src/libclang/template_parser.cpp +++ b/src/libclang/template_parser.cpp @@ -3,6 +3,9 @@ // found in the top-level directory of this distribution. #include +#include + +#include #include "libclang_visitor.hpp" #include "parse_functions.hpp" @@ -11,9 +14,9 @@ using namespace cppast; namespace { - template + template type_safe::optional 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 // so two visit calls are required @@ -34,7 +37,7 @@ namespace auto entity = detail::parse_entity(context, result, cur); if (!entity) 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"); return typename TemplateT::builder( std::unique_ptr(static_cast(entity.release()))); @@ -206,10 +209,55 @@ std::unique_ptr detail::parse_cpp_alias_template(const detail::parse { DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TypeAliasTemplateDecl, detail::assert_handler{}); - auto builder = get_builder(context, cur); + auto builder = + get_builder(context, cur, [](cpp_entity_kind k) { + return k == cpp_entity_kind::type_alias_t; + }); if (!builder) return nullptr; context.comments.match(builder.value().get(), cur); parse_parameters(builder.value(), context, cur); return builder.value().finish(*context.idx, detail::get_entity_id(cur)); } + +std::unique_ptr 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 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(static_cast(func.release()))); + builder.get().set_comment(std::move(comment)); + parse_parameters(builder, context, cur); + return builder.finish(*context.idx, detail::get_entity_id(cur)); +} diff --git a/test/cpp_alias_template.cpp b/test/cpp_alias_template.cpp index 5390004..a128730 100644 --- a/test/cpp_alias_template.cpp +++ b/test/cpp_alias_template.cpp @@ -37,41 +37,26 @@ template using h = g; )"; - auto check_parameters = - [](const cpp_alias_template& alias, - std::initializer_list> 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; auto file = parse(idx, "cpp_alias_template.cpp", code); auto count = test_visit(*file, [&](const cpp_alias_template& alias) { 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(), *cpp_builtin_type::build("int"))); } else if (alias.name() == "b") { - check_parameters(alias, {{cpp_entity_kind::non_type_template_parameter_t, "I"}, - {cpp_entity_kind::template_type_parameter_t, "T"}}); + check_template_parameters(alias, {{cpp_entity_kind::non_type_template_parameter_t, "I"}, + {cpp_entity_kind::template_type_parameter_t, "T"}}); REQUIRE(equal_types(idx, alias.type_alias().underlying_type(), *cpp_template_parameter_type::build( cpp_template_type_parameter_ref(cpp_entity_id(""), "T")))); } 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( cpp_template_type_parameter_ref(cpp_entity_id(""), "T")); REQUIRE(equal_types(idx, alias.type_alias().underlying_type(), @@ -80,7 +65,7 @@ using h = g; } 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_ref(cpp_entity_id(""), "a")); @@ -89,7 +74,8 @@ using h = g; } 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_ref(cpp_entity_id(""), "b")); @@ -100,7 +86,8 @@ using h = g; } 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_ref(cpp_entity_id(""), "b")); @@ -111,8 +98,9 @@ using h = g; } else if (alias.name() == "g") { - check_parameters(alias, {{cpp_entity_kind::template_type_parameter_t, "T"}, - {cpp_entity_kind::template_template_parameter_t, "Templ"}}); + check_template_parameters(alias, + {{cpp_entity_kind::template_type_parameter_t, "T"}, + {cpp_entity_kind::template_template_parameter_t, "Templ"}}); cpp_template_instantiation_type::builder builder( cpp_template_ref(cpp_entity_id(""), "Templ")); @@ -121,7 +109,7 @@ using h = g; } 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_ref(cpp_entity_id(""), "g")); diff --git a/test/cpp_function_template.cpp b/test/cpp_function_template.cpp new file mode 100644 index 0000000..303f756 --- /dev/null +++ b/test/cpp_function_template.cpp @@ -0,0 +1,142 @@ +// Copyright (C) 2017 Jonathan Müller +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#include + +#include + +#include "test_parser.hpp" + +using namespace cppast; + +TEST_CASE("cpp_function_template") +{ + // only check templated related stuff + auto code = R"( +template +T a(const T& t); + +template +using type = int; + +struct d +{ + template + static type b(T); + + template + auto c() -> T; + + template + operator T() const; + + template + d(const T&); +}; +)"; + + cpp_entity_index idx; + auto file = parse(idx, "cpp_function_template.cpp", code); + auto count = test_visit(*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(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(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(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(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(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); +} diff --git a/test/cpp_member_function.cpp b/test/cpp_member_function.cpp index 86352b6..dc42d12 100644 --- a/test/cpp_member_function.cpp +++ b/test/cpp_member_function.cpp @@ -151,7 +151,6 @@ struct foo cpp_entity_index idx; auto file = parse(idx, "cpp_conversion_op.cpp", code); auto count = test_visit(*file, [&](const cpp_conversion_op& op) { - REQUIRE(op.name().empty()); REQUIRE(count_children(op) == 0u); REQUIRE(!op.is_variadic()); REQUIRE(op.body_kind() == cpp_function_declaration); @@ -161,20 +160,21 @@ struct foo if (!op.is_explicit() && !op.is_constexpr()) { + REQUIRE(op.name() == "operator int &"); REQUIRE(equal_types(idx, op.return_type(), *cpp_reference_type::build(cpp_builtin_type::build("int"), cpp_ref_lvalue))); REQUIRE(op.cv_qualifier() == cpp_cv_none); - REQUIRE(!op.is_explicit()); - REQUIRE(!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(op.cv_qualifier() == cpp_cv_const); } else if (!op.is_explicit() && op.is_constexpr()) { + REQUIRE(op.name() == "operator ns::type"); REQUIRE(equal_types(idx, op.return_type(), *cpp_user_defined_type::build( cpp_type_ref(cpp_entity_id(""), "ns::type")))); diff --git a/test/test_parser.hpp b/test/test_parser.hpp index 286a6a9..87ef687 100644 --- a/test/test_parser.hpp +++ b/test/test_parser.hpp @@ -100,4 +100,20 @@ inline bool equal_expressions(const cppast::cpp_expression& parsed, return false; } +template +void check_template_parameters( + const T& templ, std::initializer_list> 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