Parse template parameters and part of alias template
This commit is contained in:
parent
d21d017d8a
commit
cd4a25e959
14 changed files with 688 additions and 45 deletions
|
|
@ -14,6 +14,8 @@ namespace cppast
|
||||||
class cpp_alias_template final : public cpp_template
|
class cpp_alias_template final : public cpp_template
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static cpp_entity_kind kind() noexcept;
|
||||||
|
|
||||||
/// Builder for [cppast::cpp_alias_template]().
|
/// Builder for [cppast::cpp_alias_template]().
|
||||||
class builder : public basic_builder<cpp_alias_template, cpp_type_alias>
|
class builder : public basic_builder<cpp_alias_template, cpp_type_alias>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,8 @@ namespace cppast
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
basic_builder(basic_builder&&) = default;
|
||||||
|
|
||||||
/// \effects Adds a parameter.
|
/// \effects Adds a parameter.
|
||||||
void add_parameter(std::unique_ptr<cpp_template_parameter> 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));
|
.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.
|
/// \effects Registers the template.
|
||||||
/// \returns The finished template.
|
/// \returns The finished template.
|
||||||
std::unique_ptr<T> finish(const cpp_entity_index& idx, cpp_entity_id id)
|
std::unique_ptr<T> finish(const cpp_entity_index& idx, cpp_entity_id id)
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,8 @@ namespace cppast
|
||||||
class cpp_template_type_parameter final : public cpp_template_parameter
|
class cpp_template_type_parameter final : public cpp_template_parameter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static cpp_entity_kind kind() noexcept;
|
||||||
|
|
||||||
/// \returns A newly created and registered template type parameter.
|
/// \returns A newly created and registered template type parameter.
|
||||||
/// \notes The `default_type` may be `nullptr` in which case the parameter has no default.
|
/// \notes The `default_type` may be `nullptr` in which case the parameter has no default.
|
||||||
static std::unique_ptr<cpp_template_type_parameter> build(
|
static std::unique_ptr<cpp_template_type_parameter> build(
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,12 @@
|
||||||
|
|
||||||
using namespace cppast;
|
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;
|
return cpp_entity_kind::alias_template_t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cpp_entity_kind cpp_alias_template::do_get_entity_kind() const noexcept
|
||||||
|
{
|
||||||
|
return kind();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,11 +31,16 @@ std::unique_ptr<cpp_template_type_parameter> cpp_template_type_parameter::build(
|
||||||
return result;
|
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;
|
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)
|
bool detail::cpp_template_parameter_ref_predicate::operator()(const cpp_entity& e)
|
||||||
{
|
{
|
||||||
return e.kind() == cpp_entity_kind::template_type_parameter_t;
|
return e.kind() == cpp_entity_kind::template_type_parameter_t;
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,13 @@ void detail::print_cursor_info(const CXCursor& cur) noexcept
|
||||||
cxstring(clang_getCursorUSR(cur)).c_str());
|
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,
|
void detail::print_tokens(const CXTranslationUnit& tu, const CXFile& file,
|
||||||
const CXCursor& cur) noexcept
|
const CXCursor& cur) noexcept
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ namespace cppast
|
||||||
|
|
||||||
void print_cursor_info(const CXCursor& cur) noexcept;
|
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,
|
void print_tokens(const CXTranslationUnit& tu, const CXFile& file,
|
||||||
const CXCursor& cur) noexcept;
|
const CXCursor& cur) noexcept;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
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);
|
auto kind = clang_getCursorKind(cur);
|
||||||
switch (kind)
|
switch (kind)
|
||||||
|
|
@ -90,7 +90,7 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
|
||||||
|
|
||||||
case CXCursor_TypeAliasDecl:
|
case CXCursor_TypeAliasDecl:
|
||||||
case CXCursor_TypedefDecl:
|
case CXCursor_TypedefDecl:
|
||||||
return parse_cpp_type_alias(context, cur);
|
return parse_cpp_type_alias(context, cur, as_template);
|
||||||
case CXCursor_EnumDecl:
|
case CXCursor_EnumDecl:
|
||||||
return parse_cpp_enum(context, cur);
|
return parse_cpp_enum(context, cur);
|
||||||
case CXCursor_ClassDecl:
|
case CXCursor_ClassDecl:
|
||||||
|
|
@ -117,6 +117,9 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
|
||||||
case CXCursor_Destructor:
|
case CXCursor_Destructor:
|
||||||
return parse_cpp_destructor(context, cur);
|
return parse_cpp_destructor(context, cur);
|
||||||
|
|
||||||
|
case CXCursor_TypeAliasTemplateDecl:
|
||||||
|
return parse_cpp_alias_template(context, cur);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,13 @@ namespace cppast
|
||||||
|
|
||||||
std::unique_ptr<cpp_type> parse_type(const parse_context& context, const CXType& type);
|
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,
|
std::unique_ptr<cpp_expression> parse_expression(const parse_context& context,
|
||||||
const CXCursor& cur);
|
const CXCursor& cur);
|
||||||
// parse the expression starting at the current token in the stream
|
// parse the expression starting at the current token in the stream
|
||||||
|
|
@ -93,7 +100,7 @@ namespace cppast
|
||||||
const CXCursor& cur);
|
const CXCursor& cur);
|
||||||
|
|
||||||
std::unique_ptr<cpp_entity> parse_cpp_type_alias(const parse_context& context,
|
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,
|
std::unique_ptr<cpp_entity> parse_cpp_enum(const parse_context& context,
|
||||||
const CXCursor& cur);
|
const CXCursor& cur);
|
||||||
std::unique_ptr<cpp_entity> parse_cpp_class(const parse_context& context,
|
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,
|
std::unique_ptr<cpp_entity> parse_cpp_destructor(const parse_context& context,
|
||||||
const CXCursor& cur);
|
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
|
} // namespace cppast::detail
|
||||||
|
|
||||||
|
|
|
||||||
211
src/libclang/template_parser.cpp
Normal file
211
src/libclang/template_parser.cpp
Normal 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));
|
||||||
|
}
|
||||||
|
|
@ -80,14 +80,17 @@ namespace
|
||||||
// this function returns the actual CXSourceRange that covers all parts required for parsing
|
// this function returns the actual CXSourceRange that covers all parts required for parsing
|
||||||
// might include more tokens
|
// might include more tokens
|
||||||
// this function is the reason you shouldn't use libclang
|
// 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 extent = clang_getCursorExtent(cur);
|
||||||
auto begin = clang_getRangeStart(extent);
|
auto begin = clang_getRangeStart(extent);
|
||||||
auto end = clang_getRangeEnd(extent);
|
auto end = clang_getRangeEnd(extent);
|
||||||
|
|
||||||
if (cursor_is_function(clang_getCursorKind(cur))
|
auto kind = clang_getCursorKind(cur);
|
||||||
|| cursor_is_function(clang_getTemplateCursorKind(cur)))
|
if (cursor_is_function(kind) || cursor_is_function(clang_getTemplateCursorKind(cur)))
|
||||||
{
|
{
|
||||||
auto range_shrunk = false;
|
auto range_shrunk = false;
|
||||||
|
|
||||||
|
|
@ -114,24 +117,17 @@ namespace
|
||||||
end = get_next_location(tu, file, end);
|
end = get_next_location(tu, file, end);
|
||||||
} while (!token_after_is(tu, file, cur, 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
|
// necessary for some reason
|
||||||
begin = get_next_location(tu, file, begin, -1);
|
begin = get_next_location(tu, file, begin, -1);
|
||||||
}
|
}
|
||||||
else if (clang_getCursorKind(cur) == CXCursor_TemplateTypeParameter
|
else if (kind == CXCursor_TemplateTypeParameter && token_after_is(tu, file, cur, end, "("))
|
||||||
|| clang_getCursorKind(cur) == CXCursor_NonTypeTemplateParameter
|
|
||||||
|| clang_getCursorKind(cur) == CXCursor_TemplateTemplateParameter
|
|
||||||
|| clang_getCursorKind(cur) == CXCursor_ParmDecl)
|
|
||||||
{
|
|
||||||
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
|
// if you have decltype as default argument for a type template parameter
|
||||||
// libclang doesn't include the parameters
|
// libclang doesn't include the parameters
|
||||||
auto next = get_next_location(tu, file, end);
|
auto next = get_next_location(tu, file, end);
|
||||||
auto prev = end;
|
auto prev = end;
|
||||||
for (auto paren_count = 1; paren_count != 0;
|
for (auto paren_count = 1; paren_count != 0; next = get_next_location(tu, file, next))
|
||||||
next = get_next_location(tu, file, next))
|
|
||||||
{
|
{
|
||||||
if (token_after_is(tu, file, cur, next, "("))
|
if (token_after_is(tu, file, cur, next, "("))
|
||||||
++paren_count;
|
++paren_count;
|
||||||
|
|
@ -141,10 +137,49 @@ namespace
|
||||||
}
|
}
|
||||||
end = prev;
|
end = prev;
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
else if (clang_isExpression(clang_getCursorKind(cur))
|
|
||||||
|| clang_getCursorKind(cur) == CXCursor_CXXBaseSpecifier
|
// second: skip until end of parameter
|
||||||
|| clang_getCursorKind(cur) == CXCursor_FieldDecl)
|
// 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
|
// need to shrink range by one
|
||||||
end = get_next_location(tu, file, end, -1);
|
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)
|
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);
|
simple_tokenizer tokenizer(tu, extent, cur);
|
||||||
tokens_.reserve(tokenizer.size());
|
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)
|
void detail::skip(detail::token_stream& stream, const char* str)
|
||||||
{
|
{
|
||||||
|
if (*str)
|
||||||
|
{
|
||||||
|
// non-empty
|
||||||
auto& token = stream.peek();
|
auto& token = stream.peek();
|
||||||
DEBUG_ASSERT(token == str, parse_error_handler{}, stream.cursor(),
|
DEBUG_ASSERT(token == str, parse_error_handler{}, stream.cursor(),
|
||||||
format("expected '", str, "', got '", token.c_str(), "'"));
|
format("expected '", str, "', got '", token.c_str(), "'"));
|
||||||
stream.bump();
|
stream.bump();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
|
@ -185,6 +224,8 @@ namespace
|
||||||
|
|
||||||
bool detail::skip_if(detail::token_stream& stream, const char* str, bool multi_token)
|
bool detail::skip_if(detail::token_stream& stream, const char* str, bool multi_token)
|
||||||
{
|
{
|
||||||
|
if (!*str)
|
||||||
|
return true;
|
||||||
auto save = stream.cur();
|
auto save = stream.cur();
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
|
@ -212,7 +253,7 @@ detail::token_iterator detail::find_closing_bracket(detail::token_stream stream)
|
||||||
close_bracket = "]";
|
close_bracket = "]";
|
||||||
else if (skip_if(stream, "<"))
|
else if (skip_if(stream, "<"))
|
||||||
{
|
{
|
||||||
close_bracket = "<";
|
close_bracket = ">";
|
||||||
template_bracket = true;
|
template_bracket = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -76,15 +76,28 @@ namespace cppast
|
||||||
return tokens_.end();
|
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:
|
private:
|
||||||
std::vector<token> tokens_;
|
std::vector<token> tokens_;
|
||||||
|
bool unmunch_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class token_stream
|
class token_stream
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit token_stream(const tokenizer& tokenizer, const CXCursor& cur)
|
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;
|
cur_ = iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool unmunch() const noexcept
|
||||||
|
{
|
||||||
|
return unmunch_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CXCursor cursor_;
|
CXCursor cursor_;
|
||||||
token_iterator begin_, cur_, end_;
|
token_iterator begin_, cur_, end_;
|
||||||
|
bool unmunch_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// skips the next token
|
// skips the next token
|
||||||
|
|
|
||||||
|
|
@ -435,16 +435,51 @@ std::unique_ptr<cpp_type> detail::parse_type(const detail::parse_context& contex
|
||||||
return std::move(result);
|
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,
|
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,
|
DEBUG_ASSERT(cur.kind == CXCursor_TypeAliasDecl || cur.kind == CXCursor_TypedefDecl,
|
||||||
detail::assert_handler{});
|
detail::assert_handler{});
|
||||||
|
|
||||||
auto name = detail::get_cursor_name(cur);
|
auto name = detail::get_cursor_name(cur);
|
||||||
auto type = parse_type(context, clang_getTypedefDeclUnderlyingType(cur));
|
auto type = parse_type(context, clang_getTypedefDeclUnderlyingType(cur));
|
||||||
|
|
||||||
|
if (as_template)
|
||||||
|
return cpp_type_alias::build(name.c_str(), std::move(type));
|
||||||
|
else
|
||||||
|
{
|
||||||
auto result =
|
auto result =
|
||||||
cpp_type_alias::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type));
|
cpp_type_alias::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type));
|
||||||
context.comments.match(*result, cur);
|
context.comments.match(*result, cur);
|
||||||
return result;
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
291
test/cpp_template_parameter.cpp
Normal file
291
test/cpp_template_parameter.cpp
Normal 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);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue