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
|
||||
{
|
||||
public:
|
||||
static cpp_entity_kind kind() noexcept;
|
||||
|
||||
/// Builder for [cppast::cpp_alias_template]().
|
||||
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.
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
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
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
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