Parse template parameters and part of alias template

This commit is contained in:
Jonathan Müller 2017-03-23 09:10:14 +01:00
commit cd4a25e959
14 changed files with 688 additions and 45 deletions

View file

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

View file

@ -39,6 +39,8 @@ namespace cppast
{
}
basic_builder(basic_builder&&) = default;
/// \effects Adds a parameter.
void add_parameter(std::unique_ptr<cpp_template_parameter> parameter)
{
@ -46,6 +48,12 @@ namespace cppast
.parameters_.push_back(*template_entity, std::move(parameter));
}
/// \returns The not yet finished template.
T& get() const noexcept
{
return *template_entity;
}
/// \effects Registers the template.
/// \returns The finished template.
std::unique_ptr<T> finish(const cpp_entity_index& idx, cpp_entity_id id)

View file

@ -48,6 +48,8 @@ namespace cppast
class cpp_template_type_parameter final : public cpp_template_parameter
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly created and registered template type parameter.
/// \notes The `default_type` may be `nullptr` in which case the parameter has no default.
static std::unique_ptr<cpp_template_type_parameter> build(

View file

@ -8,7 +8,12 @@
using namespace cppast;
cpp_entity_kind cpp_alias_template::do_get_entity_kind() const noexcept
cpp_entity_kind cpp_alias_template::kind() noexcept
{
return cpp_entity_kind::alias_template_t;
}
cpp_entity_kind cpp_alias_template::do_get_entity_kind() const noexcept
{
return kind();
}

View file

@ -31,11 +31,16 @@ std::unique_ptr<cpp_template_type_parameter> cpp_template_type_parameter::build(
return result;
}
cpp_entity_kind cpp_template_type_parameter::do_get_entity_kind() const noexcept
cpp_entity_kind cpp_template_type_parameter::kind() noexcept
{
return cpp_entity_kind::template_type_parameter_t;
}
cpp_entity_kind cpp_template_type_parameter::do_get_entity_kind() const noexcept
{
return kind();
}
bool detail::cpp_template_parameter_ref_predicate::operator()(const cpp_entity& e)
{
return e.kind() == cpp_entity_kind::template_type_parameter_t;

View file

@ -39,6 +39,13 @@ void detail::print_cursor_info(const CXCursor& cur) noexcept
cxstring(clang_getCursorUSR(cur)).c_str());
}
void detail::print_type_info(const CXType& type) noexcept
{
std::lock_guard<std::mutex> lock(mtx);
std::printf("[debug] type '%s' (%s)\n", cxstring(clang_getTypeSpelling(type)).c_str(),
get_type_kind_spelling(type).c_str());
}
void detail::print_tokens(const CXTranslationUnit& tu, const CXFile& file,
const CXCursor& cur) noexcept
{

View file

@ -19,6 +19,8 @@ namespace cppast
void print_cursor_info(const CXCursor& cur) noexcept;
void print_type_info(const CXType& type) noexcept;
void print_tokens(const CXTranslationUnit& tu, const CXFile& file,
const CXCursor& cur) noexcept;
}

View file

@ -68,7 +68,7 @@ void detail::comment_context::match(cpp_entity& e, unsigned line) const
}
std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& context,
const CXCursor& cur) try
const CXCursor& cur, bool as_template) try
{
auto kind = clang_getCursorKind(cur);
switch (kind)
@ -90,7 +90,7 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
case CXCursor_TypeAliasDecl:
case CXCursor_TypedefDecl:
return parse_cpp_type_alias(context, cur);
return parse_cpp_type_alias(context, cur, as_template);
case CXCursor_EnumDecl:
return parse_cpp_enum(context, cur);
case CXCursor_ClassDecl:
@ -117,6 +117,9 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
case CXCursor_Destructor:
return parse_cpp_destructor(context, cur);
case CXCursor_TypeAliasTemplateDecl:
return parse_cpp_alias_template(context, cur);
default:
break;
}

View file

@ -60,6 +60,13 @@ namespace cppast
std::unique_ptr<cpp_type> parse_type(const parse_context& context, const CXType& type);
// parse the type starting at the current token stream
// and ends at the given iterator
// this is required for situations where there is no type exposed,
// like default type of a template type parameter
std::unique_ptr<cpp_type> parse_raw_type(const parse_context& context, token_stream& stream,
token_iterator end);
std::unique_ptr<cpp_expression> parse_expression(const parse_context& context,
const CXCursor& cur);
// parse the expression starting at the current token in the stream
@ -93,7 +100,7 @@ namespace cppast
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_type_alias(const parse_context& context,
const CXCursor& cur);
const CXCursor& cur, bool as_template);
std::unique_ptr<cpp_entity> parse_cpp_enum(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_class(const parse_context& context,
@ -116,7 +123,12 @@ namespace cppast
std::unique_ptr<cpp_entity> parse_cpp_destructor(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_entity(const parse_context& context, const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_alias_template(const parse_context& context,
const CXCursor& cur);
// as_template: true, iff currently parsing a template
std::unique_ptr<cpp_entity> parse_entity(const parse_context& context, const CXCursor& cur,
bool as_template = false);
}
} // namespace cppast::detail

View file

@ -0,0 +1,211 @@
// 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_alias_template.hpp>
#include "libclang_visitor.hpp"
#include "parse_functions.hpp"
using namespace cppast;
namespace
{
template <typename TemplateT, typename EntityT>
typename TemplateT::builder get_builder(const detail::parse_context& context,
const CXCursor& cur)
{
// we need the actual entity first, then the parameters
// so two visit calls are required
auto result = clang_getNullCursor();
detail::visit_children(cur, [&](const CXCursor& child) {
auto kind = clang_getCursorKind(child);
if (kind == CXCursor_TemplateTypeParameter || kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter)
return;
DEBUG_ASSERT(clang_Cursor_isNull(result), detail::parse_error_handler{}, cur,
"unexpected child of template");
result = child;
});
DEBUG_ASSERT(!clang_Cursor_isNull(result), detail::parse_error_handler{}, cur,
"missing child of template");
auto entity = detail::parse_entity(context, result, true);
DEBUG_ASSERT(entity->kind() == EntityT::kind(), detail::parse_error_handler{}, cur,
"wrong child of template");
return typename TemplateT::builder(
std::unique_ptr<EntityT>(static_cast<EntityT*>(entity.release())));
}
std::unique_ptr<cpp_template_parameter> parse_type_parameter(
const detail::parse_context& context, const CXCursor& cur)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TemplateTypeParameter,
detail::assert_handler{});
detail::tokenizer tokenizer(context.tu, context.file, cur);
detail::token_stream stream(tokenizer, cur);
auto name = detail::get_cursor_name(cur);
// syntax: typename/class [...] name [= ...]
auto keyword = cpp_template_keyword::keyword_class;
if (detail::skip_if(stream, "typename"))
keyword = cpp_template_keyword::keyword_typename;
else
detail::skip(stream, "class");
auto variadic = false;
if (detail::skip_if(stream, "..."))
variadic = true;
if (stream.peek() != "=")
detail::skip(stream, name.c_str());
std::unique_ptr<cpp_type> def;
if (detail::skip_if(stream, "="))
// default type
def = detail::parse_raw_type(context, stream, stream.end());
return cpp_template_type_parameter::build(*context.idx, detail::get_entity_id(cur),
name.c_str(), keyword, variadic, std::move(def));
}
std::unique_ptr<cpp_template_parameter> parse_non_type_parameter(
const detail::parse_context& context, const CXCursor& cur)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_NonTypeTemplateParameter,
detail::assert_handler{});
auto name = detail::get_cursor_name(cur);
auto type = clang_getCursorType(cur);
std::unique_ptr<cpp_expression> def;
detail::visit_children(cur, [&](const CXCursor& child) {
DEBUG_ASSERT(clang_isExpression(clang_getCursorKind(child)) && !def,
detail::parse_error_handler{}, cur,
"unexpected child cursor of non type template parameter");
def = detail::parse_expression(context, child);
});
detail::tokenizer tokenizer(context.tu, context.file, cur);
detail::token_stream stream(tokenizer, cur);
// see if it is variadic
// syntax a): some-tokens ... name some-tokens
// syntax b): some-tokens (some-tokens ... name) some-tokens-or-...
// name might be empty, so can't loop until it occurs
// some-tokens will not contain ... or parenthesis, however
auto is_variadic = false;
for (; !stream.done(); stream.bump())
{
if (stream.peek() == "...")
{
is_variadic = true;
break;
}
else if (stream.peek() == ")")
break;
}
return cpp_non_type_template_parameter::build(*context.idx, detail::get_entity_id(cur),
name.c_str(),
detail::parse_type(context, type),
is_variadic, std::move(def));
}
std::unique_ptr<cpp_template_template_parameter> parse_template_parameter(
const detail::parse_context& context, const CXCursor& cur)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TemplateTemplateParameter,
detail::assert_handler{});
detail::tokenizer tokenizer(context.tu, context.file, cur);
detail::token_stream stream(tokenizer, cur);
auto name = detail::get_cursor_name(cur);
// syntax: template <…> class/typename [...] name [= …]
detail::skip(stream, "template");
detail::skip_brackets(stream);
auto keyword = cpp_template_keyword::keyword_class;
if (detail::skip_if(stream, "typename"))
keyword = cpp_template_keyword::keyword_typename;
else
detail::skip(stream, "class");
auto is_variadic = detail::skip_if(stream, "...");
detail::skip(stream, name.c_str());
// now we can create the builder
cpp_template_template_parameter::builder builder(name.c_str(), is_variadic);
builder.keyword(keyword);
// look for parameters and default
detail::visit_children(cur, [&](const CXCursor& child) {
auto kind = clang_getCursorKind(child);
if (kind == CXCursor_TemplateTypeParameter)
builder.add_parameter(parse_type_parameter(context, child));
else if (kind == CXCursor_NonTypeTemplateParameter)
builder.add_parameter(parse_non_type_parameter(context, child));
else if (kind == CXCursor_TemplateTemplateParameter)
builder.add_parameter(parse_template_parameter(context, child));
else if (kind == CXCursor_TemplateRef)
{
auto target = clang_getCursorReferenced(child);
// stream is after the keyword
// syntax: = default
detail::skip(stream, "=");
std::string spelling;
while (!stream.done())
spelling += stream.get().c_str();
if (stream.unmunch())
{
DEBUG_ASSERT(!spelling.empty() && spelling.back() == '>',
detail::assert_handler{});
spelling.pop_back();
DEBUG_ASSERT(!spelling.empty() && spelling.back() == '>',
detail::assert_handler{});
}
builder.default_template(
cpp_template_ref(detail::get_entity_id(target), std::move(spelling)));
}
else
DEBUG_ASSERT(clang_isReference(kind), detail::parse_error_handler{}, cur,
"unexpected child of template template parameter");
});
return builder.finish(*context.idx, detail::get_entity_id(cur));
}
template <class Builder>
void parse_parameters(Builder& builder, const detail::parse_context& context,
const CXCursor& cur)
{
// now visit to get the parameters
detail::visit_children(cur, [&](const CXCursor& child) {
auto kind = clang_getCursorKind(child);
if (kind == CXCursor_TemplateTypeParameter)
builder.add_parameter(parse_type_parameter(context, child));
else if (kind == CXCursor_NonTypeTemplateParameter)
builder.add_parameter(parse_non_type_parameter(context, child));
else if (kind == CXCursor_TemplateTemplateParameter)
builder.add_parameter(parse_template_parameter(context, child));
});
}
}
std::unique_ptr<cpp_entity> detail::parse_cpp_alias_template(const detail::parse_context& context,
const CXCursor& cur)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TypeAliasTemplateDecl,
detail::assert_handler{});
auto builder = get_builder<cpp_alias_template, cpp_type_alias>(context, cur);
context.comments.match(builder.get(), cur);
parse_parameters(builder, context, cur);
return builder.finish(*context.idx, detail::get_entity_id(cur));
}

View file

@ -80,14 +80,17 @@ namespace
// this function returns the actual CXSourceRange that covers all parts required for parsing
// might include more tokens
// this function is the reason you shouldn't use libclang
CXSourceRange get_extent(const CXTranslationUnit& tu, const CXFile& file, const CXCursor& cur)
CXSourceRange get_extent(const CXTranslationUnit& tu, const CXFile& file, const CXCursor& cur,
bool& unmunch)
{
unmunch = false;
auto extent = clang_getCursorExtent(cur);
auto begin = clang_getRangeStart(extent);
auto end = clang_getRangeEnd(extent);
if (cursor_is_function(clang_getCursorKind(cur))
|| cursor_is_function(clang_getTemplateCursorKind(cur)))
auto kind = clang_getCursorKind(cur);
if (cursor_is_function(kind) || cursor_is_function(clang_getTemplateCursorKind(cur)))
{
auto range_shrunk = false;
@ -114,37 +117,69 @@ namespace
end = get_next_location(tu, file, end);
} while (!token_after_is(tu, file, cur, end, ";"));
}
else if (clang_getCursorKind(cur) == CXCursor_CXXMethod)
else if (kind == CXCursor_CXXMethod)
// necessary for some reason
begin = get_next_location(tu, file, begin, -1);
}
else if (clang_getCursorKind(cur) == CXCursor_TemplateTypeParameter
|| clang_getCursorKind(cur) == CXCursor_NonTypeTemplateParameter
|| clang_getCursorKind(cur) == CXCursor_TemplateTemplateParameter
|| clang_getCursorKind(cur) == CXCursor_ParmDecl)
else if (kind == CXCursor_TemplateTypeParameter && token_after_is(tu, file, cur, end, "("))
{
if (clang_getCursorKind(cur) == CXCursor_TemplateTypeParameter
&& token_after_is(tu, file, cur, end, "("))
// if you have decltype as default argument for a type template parameter
// libclang doesn't include the parameters
auto next = get_next_location(tu, file, end);
auto prev = end;
for (auto paren_count = 1; paren_count != 0; next = get_next_location(tu, file, next))
{
// if you have decltype as default argument for a type template parameter
// libclang doesn't include the parameters
auto next = get_next_location(tu, file, end);
auto prev = end;
for (auto paren_count = 1; paren_count != 0;
next = get_next_location(tu, file, next))
{
if (token_after_is(tu, file, cur, next, "("))
++paren_count;
else if (token_after_is(tu, file, cur, next, ")"))
--paren_count;
prev = next;
}
end = prev;
if (token_after_is(tu, file, cur, next, "("))
++paren_count;
else if (token_after_is(tu, file, cur, next, ")"))
--paren_count;
prev = next;
}
end = prev;
}
else if (clang_isExpression(clang_getCursorKind(cur))
|| clang_getCursorKind(cur) == CXCursor_CXXBaseSpecifier
|| clang_getCursorKind(cur) == CXCursor_FieldDecl)
else if (kind == CXCursor_TemplateTemplateParameter
&& token_after_is(tu, file, cur, end, "<"))
{
// if you have a template template parameter in a template template parameter,
// the tokens are all messed up, only contain the `template`
// first: skip to closing angle bracket
// luckily no need to handle expressions here
auto next = get_next_location(tu, file, end, 2);
for (auto angle_count = 1; angle_count != 0; next = get_next_location(tu, file, next))
{
if (token_after_is(tu, file, cur, next, ">"))
--angle_count;
else if (token_after_is(tu, file, cur, next, ">>"))
angle_count -= 2;
else if (token_after_is(tu, file, cur, next, "<"))
++angle_count;
}
// second: skip until end of parameter
// no need to handle default, so look for '>' or ','
while (!token_after_is(tu, file, cur, next, ">")
&& !token_after_is(tu, file, cur, next, ","))
next = get_next_location(tu, file, next);
// now we found the proper end of the token
end = get_next_location(tu, file, next, -1);
}
else if ((kind == CXCursor_TemplateTypeParameter
|| kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter)
&& !token_after_is(tu, file, cur, end, ">")
&& !token_after_is(tu, file, cur, end, ","))
{
DEBUG_ASSERT(token_after_is(tu, file, cur, get_next_location(tu, file, end, -2), ">>"),
detail::assert_handler{});
unmunch = true;
// need to shrink range anyway
end = get_next_location(tu, file, end, -1);
}
else if (clang_isExpression(kind) || kind == CXCursor_CXXBaseSpecifier
|| kind == CXCursor_FieldDecl || kind == CXCursor_TemplateTypeParameter
|| kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter)
// need to shrink range by one
end = get_next_location(tu, file, end, -1);
@ -154,7 +189,7 @@ namespace
detail::tokenizer::tokenizer(const CXTranslationUnit& tu, const CXFile& file, const CXCursor& cur)
{
auto extent = get_extent(tu, file, cur);
auto extent = get_extent(tu, file, cur, unmunch_);
simple_tokenizer tokenizer(tu, extent, cur);
tokens_.reserve(tokenizer.size());
@ -164,10 +199,14 @@ detail::tokenizer::tokenizer(const CXTranslationUnit& tu, const CXFile& file, co
void detail::skip(detail::token_stream& stream, const char* str)
{
auto& token = stream.peek();
DEBUG_ASSERT(token == str, parse_error_handler{}, stream.cursor(),
format("expected '", str, "', got '", token.c_str(), "'"));
stream.bump();
if (*str)
{
// non-empty
auto& token = stream.peek();
DEBUG_ASSERT(token == str, parse_error_handler{}, stream.cursor(),
format("expected '", str, "', got '", token.c_str(), "'"));
stream.bump();
}
}
namespace
@ -185,6 +224,8 @@ namespace
bool detail::skip_if(detail::token_stream& stream, const char* str, bool multi_token)
{
if (!*str)
return true;
auto save = stream.cur();
do
{
@ -212,7 +253,7 @@ detail::token_iterator detail::find_closing_bracket(detail::token_stream stream)
close_bracket = "]";
else if (skip_if(stream, "<"))
{
close_bracket = "<";
close_bracket = ">";
template_bracket = true;
}
else

View file

@ -76,15 +76,28 @@ namespace cppast
return tokens_.end();
}
// if it returns true, the last token is ">>",
// but should haven been ">"
// only a problem for template parameters
bool unmunch() const noexcept
{
return unmunch_;
}
private:
std::vector<token> tokens_;
bool unmunch_;
};
class token_stream
{
public:
explicit token_stream(const tokenizer& tokenizer, const CXCursor& cur)
: cursor_(cur), begin_(tokenizer.begin()), cur_(begin_), end_(tokenizer.end())
: cursor_(cur),
begin_(tokenizer.begin()),
cur_(begin_),
end_(tokenizer.end()),
unmunch_(tokenizer.unmunch())
{
}
@ -144,9 +157,15 @@ namespace cppast
cur_ = iter;
}
bool unmunch() const noexcept
{
return unmunch_;
}
private:
CXCursor cursor_;
token_iterator begin_, cur_, end_;
bool unmunch_;
};
// skips the next token

View file

@ -435,16 +435,51 @@ std::unique_ptr<cpp_type> detail::parse_type(const detail::parse_context& contex
return std::move(result);
}
namespace
{
bool is_identifier(char c)
{
return std::isalnum(c) || c == '_';
}
}
std::unique_ptr<cpp_type> detail::parse_raw_type(const detail::parse_context&,
detail::token_stream& stream,
detail::token_iterator end)
{
std::string result;
while (stream.cur() != end)
{
auto& token = stream.get();
if (!result.empty() && is_identifier(result.back()) && is_identifier(token.value()[0u]))
result += ' ';
result += token.c_str();
}
if (stream.unmunch())
{
DEBUG_ASSERT(!result.empty() && result.back() == '>', detail::assert_handler{});
result.pop_back();
DEBUG_ASSERT(!result.empty() && result.back() == '>', detail::assert_handler{});
}
return cpp_unexposed_type::build(std::move(result));
}
std::unique_ptr<cpp_entity> detail::parse_cpp_type_alias(const detail::parse_context& context,
const CXCursor& cur)
const CXCursor& cur, bool as_template)
{
DEBUG_ASSERT(cur.kind == CXCursor_TypeAliasDecl || cur.kind == CXCursor_TypedefDecl,
detail::assert_handler{});
auto name = detail::get_cursor_name(cur);
auto type = parse_type(context, clang_getTypedefDeclUnderlyingType(cur));
auto result =
cpp_type_alias::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type));
context.comments.match(*result, cur);
return result;
if (as_template)
return cpp_type_alias::build(name.c_str(), std::move(type));
else
{
auto result =
cpp_type_alias::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type));
context.comments.match(*result, cur);
return result;
}
}

View file

@ -0,0 +1,291 @@
// 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_template_parameter.hpp>
#include <cppast/cpp_alias_template.hpp>
#include <cppast/cpp_function_type.hpp>
#include "test_parser.hpp"
using namespace cppast;
TEST_CASE("cpp_template_type_parameter")
{
auto code = R"(
template <typename A>
using a = void;
template <class ... B>
using b = void;
template <typename = const int*>
using c = void;
// libclang workaround when decltype here
template <class D = decltype(1 + 3)>
using d = void;
// maximal munch here
template <typename E = a<void>>
using e = void;
)";
cpp_entity_index idx;
auto file = parse(idx, "cpp_template_type_parameter.cpp", code);
auto count = test_visit<cpp_alias_template>(*file, [&](const cpp_alias_template& alias) {
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(),
*cpp_builtin_type::build("void")));
for (auto& p : alias.parameters())
{
REQUIRE(p.kind() == cpp_entity_kind::template_type_parameter_t);
auto& param = static_cast<const cpp_template_type_parameter&>(p);
if (param.name() == "A")
{
REQUIRE(alias.name() == "a");
REQUIRE(param.keyword() == cpp_template_keyword::keyword_typename);
REQUIRE(!param.is_variadic());
REQUIRE(!param.default_type());
}
else if (param.name() == "B")
{
REQUIRE(alias.name() == "b");
REQUIRE(param.keyword() == cpp_template_keyword::keyword_class);
REQUIRE(param.is_variadic());
REQUIRE(!param.default_type());
}
else if (param.name() == "")
{
REQUIRE(alias.name() == "c");
REQUIRE(param.keyword() == cpp_template_keyword::keyword_typename);
REQUIRE(!param.is_variadic());
REQUIRE(param.default_type().has_value());
REQUIRE(equal_types(idx, param.default_type().value(),
*cpp_unexposed_type::build("const int*")));
}
else if (param.name() == "D")
{
REQUIRE(alias.name() == "d");
REQUIRE(param.keyword() == cpp_template_keyword::keyword_class);
REQUIRE(!param.is_variadic());
REQUIRE(param.default_type().has_value());
REQUIRE(equal_types(idx, param.default_type().value(),
*cpp_unexposed_type::build("decltype(1+3)")));
}
else if (param.name() == "E")
{
REQUIRE(alias.name() == "e");
REQUIRE(param.keyword() == cpp_template_keyword::keyword_typename);
REQUIRE(!param.is_variadic());
REQUIRE(param.default_type().has_value());
REQUIRE(equal_types(idx, param.default_type().value(),
*cpp_unexposed_type::build("a<void>")));
}
else
REQUIRE(false);
}
});
REQUIRE(count == 5u);
}
TEST_CASE("cpp_non_type_template_parameter")
{
auto code = R"(
template <int A>
using a = void;
template <char* = nullptr>
using b = void;
template <int ... C>
using c = void;
template <void(* D)(...)>
using d = void;
)";
cpp_entity_index idx;
auto file = parse(idx, "cpp_non_type_template_parameter.cpp", code);
auto count = test_visit<cpp_alias_template>(*file, [&](const cpp_alias_template& alias) {
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(),
*cpp_builtin_type::build("void")));
for (auto& p : alias.parameters())
{
REQUIRE(p.kind() == cpp_entity_kind::non_type_template_parameter_t);
auto& param = static_cast<const cpp_non_type_template_parameter&>(p);
if (param.name() == "A")
{
REQUIRE(alias.name() == "a");
REQUIRE(equal_types(idx, param.type(), *cpp_builtin_type::build("int")));
REQUIRE(!param.is_variadic());
REQUIRE(!param.default_value());
}
else if (param.name() == "")
{
REQUIRE(alias.name() == "b");
REQUIRE(equal_types(idx, param.type(),
*cpp_pointer_type::build(cpp_builtin_type::build("char"))));
REQUIRE(!param.is_variadic());
REQUIRE(param.default_value());
REQUIRE(equal_expressions(param.default_value().value(),
*cpp_literal_expression::build(cpp_builtin_type::build(
"nullptr_t"),
"nullptr")));
}
else if (param.name() == "C")
{
REQUIRE(alias.name() == "c");
REQUIRE(equal_types(idx, param.type(), *cpp_builtin_type::build("int")));
REQUIRE(param.is_variadic());
REQUIRE(!param.default_value());
}
else if (param.name() == "D")
{
REQUIRE(alias.name() == "d");
cpp_function_type::builder builder(cpp_builtin_type::build("void"));
builder.is_variadic();
REQUIRE(equal_types(idx, param.type(), *cpp_pointer_type::build(builder.finish())));
REQUIRE(!param.is_variadic());
REQUIRE(!param.default_value());
}
else
REQUIRE(false);
}
});
REQUIRE(count == 4u);
}
TEST_CASE("cpp_template_template_parameter")
{
// no need to check parameters of template parameter
auto code = R"(
namespace ns
{
template <int I>
using def = void;
}
template <template <typename T> class A>
using a = void;
template <template <int, typename> class ... B>
using b = void;
template <template <int> class C = ns::def>
using c = void;
template <template <template <typename> class> class D = a>
using d = void;
)";
cpp_entity_index idx;
auto file = parse(idx, "cpp_template_template_parameter.cpp", code);
auto count = test_visit<cpp_alias_template>(*file, [&](const cpp_alias_template& alias) {
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(),
*cpp_builtin_type::build("void")));
if (alias.name() == "def")
return;
for (auto& p : alias.parameters())
{
REQUIRE(p.kind() == cpp_entity_kind::template_template_parameter_t);
auto& param = static_cast<const cpp_template_template_parameter&>(p);
REQUIRE(param.keyword() == cpp_template_keyword::keyword_class);
if (param.name() == "A")
{
REQUIRE(alias.name() == "a");
REQUIRE(!param.is_variadic());
REQUIRE(!param.default_template());
auto no = 0u;
for (auto& p_param : param)
{
++no;
REQUIRE(p_param.name() == "T");
REQUIRE(p_param.kind() == cpp_entity_kind::template_type_parameter_t);
}
REQUIRE(no == 1u);
}
else if (param.name() == "B")
{
REQUIRE(alias.name() == "b");
REQUIRE(param.is_variadic());
REQUIRE(!param.default_template());
auto cur = param.begin();
REQUIRE(cur != param.end());
REQUIRE(cur->name().empty());
REQUIRE(cur->kind() == cpp_entity_kind::non_type_template_parameter_t);
++cur;
REQUIRE(cur != param.end());
REQUIRE(cur->name().empty());
REQUIRE(cur->kind() == cpp_entity_kind::template_type_parameter_t);
++cur;
REQUIRE(cur == param.end());
}
else if (param.name() == "C")
{
REQUIRE(alias.name() == "c");
REQUIRE(!param.is_variadic());
REQUIRE(param.default_template());
auto def = param.default_template().value();
REQUIRE(def.name() == "ns::def");
auto entities = def.get(idx);
REQUIRE(entities.size() == 1u);
REQUIRE(entities[0]->name() == "def");
auto no = 0u;
for (auto& p_param : param)
{
++no;
REQUIRE(p_param.name() == "");
REQUIRE(p_param.kind() == cpp_entity_kind::non_type_template_parameter_t);
}
REQUIRE(no == 1u);
}
else if (param.name() == "D")
{
REQUIRE(alias.name() == "d");
REQUIRE(!param.is_variadic());
REQUIRE(param.default_template());
auto def = param.default_template().value();
REQUIRE(def.name() == "a");
auto entities = def.get(idx);
REQUIRE(entities.size() == 1u);
REQUIRE(entities[0]->name() == "a");
auto no = 0u;
for (auto& p_param : param)
{
++no;
REQUIRE(p_param.name() == "");
REQUIRE(p_param.kind() == cpp_entity_kind::template_template_parameter_t);
for (auto& p_p_param :
static_cast<const cpp_template_template_parameter&>(p_param))
{
++no;
REQUIRE(p_p_param.name() == "");
REQUIRE(p_p_param.kind() == cpp_entity_kind::template_type_parameter_t);
}
}
REQUIRE(no == 2u);
}
else
REQUIRE(false);
}
});
REQUIRE(count == 5u);
}