From cd4a25e959e737bccd83f22a21da29582464c7c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Thu, 23 Mar 2017 09:10:14 +0100 Subject: [PATCH] Parse template parameters and part of alias template --- include/cppast/cpp_alias_template.hpp | 2 + include/cppast/cpp_template.hpp | 8 + include/cppast/cpp_template_parameter.hpp | 2 + src/cpp_alias_template.cpp | 7 +- src/cpp_template_parameter.cpp | 7 +- src/libclang/debug_helper.cpp | 7 + src/libclang/debug_helper.hpp | 2 + src/libclang/parse_functions.cpp | 7 +- src/libclang/parse_functions.hpp | 16 +- src/libclang/template_parser.cpp | 211 ++++++++++++++++ src/libclang/tokenizer.cpp | 107 +++++--- src/libclang/tokenizer.hpp | 21 +- src/libclang/type_parser.cpp | 45 +++- test/cpp_template_parameter.cpp | 291 ++++++++++++++++++++++ 14 files changed, 688 insertions(+), 45 deletions(-) create mode 100644 src/libclang/template_parser.cpp create mode 100644 test/cpp_template_parameter.cpp diff --git a/include/cppast/cpp_alias_template.hpp b/include/cppast/cpp_alias_template.hpp index 06fad40..29d49a7 100644 --- a/include/cppast/cpp_alias_template.hpp +++ b/include/cppast/cpp_alias_template.hpp @@ -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 { diff --git a/include/cppast/cpp_template.hpp b/include/cppast/cpp_template.hpp index 35beb42..92cfc5d 100644 --- a/include/cppast/cpp_template.hpp +++ b/include/cppast/cpp_template.hpp @@ -39,6 +39,8 @@ namespace cppast { } + basic_builder(basic_builder&&) = default; + /// \effects Adds a parameter. void add_parameter(std::unique_ptr 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 finish(const cpp_entity_index& idx, cpp_entity_id id) diff --git a/include/cppast/cpp_template_parameter.hpp b/include/cppast/cpp_template_parameter.hpp index 82f1dad..3633a8d 100644 --- a/include/cppast/cpp_template_parameter.hpp +++ b/include/cppast/cpp_template_parameter.hpp @@ -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 build( diff --git a/src/cpp_alias_template.cpp b/src/cpp_alias_template.cpp index f907b41..7f0f20c 100644 --- a/src/cpp_alias_template.cpp +++ b/src/cpp_alias_template.cpp @@ -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(); +} diff --git a/src/cpp_template_parameter.cpp b/src/cpp_template_parameter.cpp index 7b640f2..c5c1b72 100644 --- a/src/cpp_template_parameter.cpp +++ b/src/cpp_template_parameter.cpp @@ -31,11 +31,16 @@ std::unique_ptr 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; diff --git a/src/libclang/debug_helper.cpp b/src/libclang/debug_helper.cpp index 43f2624..4d53ce5 100644 --- a/src/libclang/debug_helper.cpp +++ b/src/libclang/debug_helper.cpp @@ -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 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 { diff --git a/src/libclang/debug_helper.hpp b/src/libclang/debug_helper.hpp index 0160513..4443528 100644 --- a/src/libclang/debug_helper.hpp +++ b/src/libclang/debug_helper.hpp @@ -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; } diff --git a/src/libclang/parse_functions.cpp b/src/libclang/parse_functions.cpp index c918ec6..464897b 100644 --- a/src/libclang/parse_functions.cpp +++ b/src/libclang/parse_functions.cpp @@ -68,7 +68,7 @@ void detail::comment_context::match(cpp_entity& e, unsigned line) const } std::unique_ptr 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 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 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; } diff --git a/src/libclang/parse_functions.hpp b/src/libclang/parse_functions.hpp index df8c8c3..7e4103f 100644 --- a/src/libclang/parse_functions.hpp +++ b/src/libclang/parse_functions.hpp @@ -60,6 +60,13 @@ namespace cppast std::unique_ptr 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 parse_raw_type(const parse_context& context, token_stream& stream, + token_iterator end); + std::unique_ptr 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 parse_cpp_type_alias(const parse_context& context, - const CXCursor& cur); + const CXCursor& cur, bool as_template); std::unique_ptr parse_cpp_enum(const parse_context& context, const CXCursor& cur); std::unique_ptr parse_cpp_class(const parse_context& context, @@ -116,7 +123,12 @@ namespace cppast std::unique_ptr parse_cpp_destructor(const parse_context& context, const CXCursor& cur); - std::unique_ptr parse_entity(const parse_context& context, const CXCursor& cur); + std::unique_ptr parse_cpp_alias_template(const parse_context& context, + const CXCursor& cur); + + // as_template: true, iff currently parsing a template + std::unique_ptr parse_entity(const parse_context& context, const CXCursor& cur, + bool as_template = false); } } // namespace cppast::detail diff --git a/src/libclang/template_parser.cpp b/src/libclang/template_parser.cpp new file mode 100644 index 0000000..2077d28 --- /dev/null +++ b/src/libclang/template_parser.cpp @@ -0,0 +1,211 @@ +// Copyright (C) 2017 Jonathan Müller +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#include + +#include "libclang_visitor.hpp" +#include "parse_functions.hpp" + +using namespace cppast; + +namespace +{ + template + 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(static_cast(entity.release()))); + } + + std::unique_ptr 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 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 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 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 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 + 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 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(context, cur); + context.comments.match(builder.get(), cur); + parse_parameters(builder, context, cur); + return builder.finish(*context.idx, detail::get_entity_id(cur)); +} diff --git a/src/libclang/tokenizer.cpp b/src/libclang/tokenizer.cpp index 291476c..c32be18 100644 --- a/src/libclang/tokenizer.cpp +++ b/src/libclang/tokenizer.cpp @@ -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 diff --git a/src/libclang/tokenizer.hpp b/src/libclang/tokenizer.hpp index 4740c4c..71d9841 100644 --- a/src/libclang/tokenizer.hpp +++ b/src/libclang/tokenizer.hpp @@ -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 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 diff --git a/src/libclang/type_parser.cpp b/src/libclang/type_parser.cpp index 1c99c12..2f262a5 100644 --- a/src/libclang/type_parser.cpp +++ b/src/libclang/type_parser.cpp @@ -435,16 +435,51 @@ std::unique_ptr 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 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 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; + } } diff --git a/test/cpp_template_parameter.cpp b/test/cpp_template_parameter.cpp new file mode 100644 index 0000000..9541737 --- /dev/null +++ b/test/cpp_template_parameter.cpp @@ -0,0 +1,291 @@ +// Copyright (C) 2017 Jonathan Müller +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#include + +#include +#include + +#include "test_parser.hpp" + +using namespace cppast; + +TEST_CASE("cpp_template_type_parameter") +{ + auto code = R"( + +template +using a = void; + +template +using b = void; + +template +using c = void; + +// libclang workaround when decltype here +template +using d = void; + +// maximal munch here +template > +using e = void; +)"; + + cpp_entity_index idx; + auto file = parse(idx, "cpp_template_type_parameter.cpp", code); + auto count = test_visit(*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(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"))); + } + else + REQUIRE(false); + } + }); + REQUIRE(count == 5u); +} + +TEST_CASE("cpp_non_type_template_parameter") +{ + auto code = R"( +template +using a = void; + +template +using b = void; + +template +using c = void; + +template +using d = void; +)"; + + cpp_entity_index idx; + auto file = parse(idx, "cpp_non_type_template_parameter.cpp", code); + auto count = test_visit(*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(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 + using def = void; +} + +template