From d6f0997fb68f5cfb4cd94186bde119c008ce921f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Fri, 24 Mar 2017 16:49:31 +0100 Subject: [PATCH] Parse cpp_alias_template --- include/cppast/cpp_entity_kind.hpp | 1 + include/cppast/cpp_template_parameter.hpp | 65 ++++- src/cpp_template_parameter.cpp | 2 +- src/libclang/enum_parser.cpp | 2 +- src/libclang/expression_parser.cpp | 2 +- src/libclang/function_parser.cpp | 10 +- src/libclang/parse_functions.cpp | 5 +- src/libclang/parse_functions.hpp | 11 +- src/libclang/template_parser.cpp | 18 +- src/libclang/tokenizer.cpp | 4 +- src/libclang/type_parser.cpp | 276 +++++++++++++++++++--- src/libclang/variable_parser.cpp | 9 +- test/cpp_alias_template.cpp | 136 +++++++++++ test/cpp_type_alias.cpp | 91 +++++-- 14 files changed, 550 insertions(+), 82 deletions(-) create mode 100644 test/cpp_alias_template.cpp diff --git a/include/cppast/cpp_entity_kind.hpp b/include/cppast/cpp_entity_kind.hpp index ba4bfb9..4403c28 100644 --- a/include/cppast/cpp_entity_kind.hpp +++ b/include/cppast/cpp_entity_kind.hpp @@ -65,6 +65,7 @@ namespace cppast bool is_type(cpp_entity_kind kind) noexcept; /// \returns Whether or not a given entity kind is a C++ template. + /// \notes A template template parameter is not considered a template for this function. bool is_template(cpp_entity_kind kind) noexcept; } // namespace cppast diff --git a/include/cppast/cpp_template_parameter.hpp b/include/cppast/cpp_template_parameter.hpp index 3633a8d..7c92c1c 100644 --- a/include/cppast/cpp_template_parameter.hpp +++ b/include/cppast/cpp_template_parameter.hpp @@ -164,8 +164,8 @@ namespace cppast class cpp_template; - /// A reference to a [cppast::cpp_template](). - using cpp_template_ref = basic_cpp_entity_ref; + /// A reference to a [cppast::cpp_template]() or a [cppast::cpp_template_template_parameter](). + using cpp_template_ref = basic_cpp_entity_ref; /// A [cppast::cpp_entity]() modelling a C++ template template parameter. class cpp_template_template_parameter final @@ -243,12 +243,67 @@ namespace cppast /// An argument for a [cppast::cpp_template_parameter](). /// - /// It is a [ts::variant]() of [cppast::cpp_type]() (for [cppast::cpp_template_type_parameter]()), + /// It is based on a [ts::variant]() of [cppast::cpp_type]() (for [cppast::cpp_template_type_parameter]()), /// [cppast::cpp_expression]() (for [cppast::cpp_non_type_template_parameter]()) and [cppast::cpp_template_ref]() /// (for [cppast::cpp_template_template_parameter](). - using cpp_template_argument = + class cpp_template_argument + { + public: + /// \effects Initializes it passing a type as argument. + /// This corresponds to a [cppast::cpp_template_type_parameter](). + /// \notes This constructor only participates in overload resolution if `T` is dervied from [cppast::cpp_type](). + /// \param 1 + /// \exclude + template ::value, int>::type = 0> + cpp_template_argument(std::unique_ptr type) + : arg_(std::unique_ptr(std::move(type))) + { + } + + /// \effects Initializes it passing an expression as argument. + /// This corresponds to a [cppast::cpp_non_type_template_parameter](). + /// \notes This constructor only participates in overload resolution if `T` is dervied from [cppast::cpp_expression](). + /// \param 1 + /// \exclude + template < + typename T, + typename = typename std::enable_if::value>::type> + cpp_template_argument(std::unique_ptr expr) + : arg_(std::unique_ptr(std::move(expr))) + { + } + + /// \effects Initializes it passing a template as argument. + /// This corresponds to a [cppast::cpp_template_template_parameter](). + cpp_template_argument(cpp_template_ref templ) : arg_(std::move(templ)) + { + } + + type_safe::optional_ref type() const noexcept + { + return arg_.optional_value(type_safe::variant_type>{}) + .map([](const std::unique_ptr& type) { return type_safe::ref(*type); }); + } + + type_safe::optional_ref expression() const noexcept + { + return arg_.optional_value(type_safe::variant_type>{}) + .map([](const std::unique_ptr& expr) { + return type_safe::ref(*expr); + }); + } + + type_safe::optional_ref template_ref() const noexcept + { + return arg_.optional_value(type_safe::variant_type{}); + } + + private: type_safe::variant, std::unique_ptr, - cpp_template_ref>; + cpp_template_ref> + arg_; + }; } // namespace cppast #endif // CPPAST_CPP_TEMPLATE_PARAMETER_HPP_INCLUDED diff --git a/src/cpp_template_parameter.cpp b/src/cpp_template_parameter.cpp index c5c1b72..7c658f1 100644 --- a/src/cpp_template_parameter.cpp +++ b/src/cpp_template_parameter.cpp @@ -64,7 +64,7 @@ cpp_entity_kind cpp_non_type_template_parameter::do_get_entity_kind() const noex bool detail::cpp_template_ref_predicate::operator()(const cpp_entity& e) { - return is_template(e.kind()); + return is_template(e.kind()) || e.kind() == cpp_entity_kind::template_template_parameter_t; } cpp_entity_kind cpp_template_template_parameter::do_get_entity_kind() const noexcept diff --git a/src/libclang/enum_parser.cpp b/src/libclang/enum_parser.cpp index 91be43d..45d59fe 100644 --- a/src/libclang/enum_parser.cpp +++ b/src/libclang/enum_parser.cpp @@ -56,7 +56,7 @@ namespace std::unique_ptr type; if (detail::skip_if(stream, ":")) // parse type, explictly given one - type = detail::parse_type(context, clang_getEnumDeclIntegerType(cur)); + type = detail::parse_type(context, cur, clang_getEnumDeclIntegerType(cur)); return cpp_enum::builder(name.c_str(), scoped, std::move(type)); } diff --git a/src/libclang/expression_parser.cpp b/src/libclang/expression_parser.cpp index 975eb8f..94c3f76 100644 --- a/src/libclang/expression_parser.cpp +++ b/src/libclang/expression_parser.cpp @@ -29,7 +29,7 @@ std::unique_ptr detail::parse_expression(const detail::parse_con detail::tokenizer tokenizer(context.tu, context.file, cur); detail::token_stream stream(tokenizer, cur); - auto type = parse_type(context, clang_getCursorType(cur)); + auto type = parse_type(context, cur, clang_getCursorType(cur)); auto expr = get_expression_str(stream, stream.end()); if (kind == CXCursor_CallExpr && (expr.empty() || expr.back() != ')')) { diff --git a/src/libclang/function_parser.cpp b/src/libclang/function_parser.cpp index 1631cb6..d41c774 100644 --- a/src/libclang/function_parser.cpp +++ b/src/libclang/function_parser.cpp @@ -16,7 +16,7 @@ namespace const CXCursor& cur) { auto name = detail::get_cursor_name(cur); - auto type = detail::parse_type(context, clang_getCursorType(cur)); + auto type = detail::parse_type(context, cur, clang_getCursorType(cur)); std::unique_ptr default_value; detail::visit_children(cur, [&](const CXCursor& child) { @@ -278,7 +278,8 @@ namespace auto name = detail::get_cursor_name(cur); cpp_function::builder builder(name.c_str(), - detail::parse_type(context, clang_getCursorResultType(cur))); + detail::parse_type(context, cur, + clang_getCursorResultType(cur))); context.comments.match(builder.get(), cur); add_parameters(context, builder, cur); if (clang_Cursor_isVariadic(cur)) @@ -413,7 +414,7 @@ std::unique_ptr detail::parse_cpp_member_function(const detail::pars auto name = detail::get_cursor_name(cur); cpp_member_function::builder builder(name.c_str(), - detail::parse_type(context, + detail::parse_type(context, cur, clang_getCursorResultType(cur))); context.comments.match(builder.get(), cur); add_parameters(context, builder, cur); @@ -435,7 +436,8 @@ std::unique_ptr detail::parse_cpp_conversion_op(const detail::parse_ const CXCursor& cur) { DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_ConversionFunction, detail::assert_handler{}); - cpp_conversion_op::builder builder(detail::parse_type(context, clang_getCursorResultType(cur))); + cpp_conversion_op::builder builder( + detail::parse_type(context, cur, clang_getCursorResultType(cur))); context.comments.match(builder.get(), cur); detail::tokenizer tokenizer(context.tu, context.file, cur); diff --git a/src/libclang/parse_functions.cpp b/src/libclang/parse_functions.cpp index 464897b..e2754d7 100644 --- a/src/libclang/parse_functions.cpp +++ b/src/libclang/parse_functions.cpp @@ -68,7 +68,8 @@ 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, bool as_template) try + const CXCursor& cur, + const CXCursor& template_cur) try { auto kind = clang_getCursorKind(cur); switch (kind) @@ -90,7 +91,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, as_template); + return parse_cpp_type_alias(context, cur, template_cur); case CXCursor_EnumDecl: return parse_cpp_enum(context, cur); case CXCursor_ClassDecl: diff --git a/src/libclang/parse_functions.hpp b/src/libclang/parse_functions.hpp index 7e4103f..6d847c4 100644 --- a/src/libclang/parse_functions.hpp +++ b/src/libclang/parse_functions.hpp @@ -58,7 +58,8 @@ namespace cppast comment_context comments; }; - std::unique_ptr parse_type(const parse_context& context, const CXType& type); + std::unique_ptr parse_type(const parse_context& context, const CXCursor& cur, + const CXType& type); // parse the type starting at the current token stream // and ends at the given iterator @@ -100,7 +101,8 @@ namespace cppast const CXCursor& cur); std::unique_ptr parse_cpp_type_alias(const parse_context& context, - const CXCursor& cur, bool as_template); + const CXCursor& cur, + const CXCursor& template_cur); std::unique_ptr parse_cpp_enum(const parse_context& context, const CXCursor& cur); std::unique_ptr parse_cpp_class(const parse_context& context, @@ -127,8 +129,9 @@ namespace cppast 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); + std::unique_ptr parse_entity( + const parse_context& context, const CXCursor& cur, + const CXCursor& template_cur = clang_getNullCursor()); } } // namespace cppast::detail diff --git a/src/libclang/template_parser.cpp b/src/libclang/template_parser.cpp index 2077d28..3327a3c 100644 --- a/src/libclang/template_parser.cpp +++ b/src/libclang/template_parser.cpp @@ -12,8 +12,8 @@ using namespace cppast; namespace { template - typename TemplateT::builder get_builder(const detail::parse_context& context, - const CXCursor& cur) + type_safe::optional 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 @@ -31,7 +31,9 @@ namespace DEBUG_ASSERT(!clang_Cursor_isNull(result), detail::parse_error_handler{}, cur, "missing child of template"); - auto entity = detail::parse_entity(context, result, true); + auto entity = detail::parse_entity(context, result, cur); + if (!entity) + return type_safe::nullopt; DEBUG_ASSERT(entity->kind() == EntityT::kind(), detail::parse_error_handler{}, cur, "wrong child of template"); return typename TemplateT::builder( @@ -111,7 +113,7 @@ namespace return cpp_non_type_template_parameter::build(*context.idx, detail::get_entity_id(cur), name.c_str(), - detail::parse_type(context, type), + detail::parse_type(context, cur, type), is_variadic, std::move(def)); } @@ -205,7 +207,9 @@ std::unique_ptr detail::parse_cpp_alias_template(const detail::parse DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TypeAliasTemplateDecl, detail::assert_handler{}); auto builder = get_builder(context, cur); - context.comments.match(builder.get(), cur); - parse_parameters(builder, context, cur); - return builder.finish(*context.idx, detail::get_entity_id(cur)); + if (!builder) + return nullptr; + context.comments.match(builder.value().get(), cur); + parse_parameters(builder.value(), context, cur); + return builder.value().finish(*context.idx, detail::get_entity_id(cur)); } diff --git a/src/libclang/tokenizer.cpp b/src/libclang/tokenizer.cpp index c32be18..38e56bf 100644 --- a/src/libclang/tokenizer.cpp +++ b/src/libclang/tokenizer.cpp @@ -272,9 +272,9 @@ detail::token_iterator detail::find_closing_bracket(detail::token_stream stream) else if (paren_count == 0 && template_bracket && cur == ">>") // maximal munch bracket_count -= 2u; - else if (cur == "(") + else if (cur == "(" || cur == "{" || cur == "[") ++paren_count; - else if (cur == ")") + else if (cur == ")" || cur == "}" || cur == "]") --paren_count; } stream.bump_back(); diff --git a/src/libclang/type_parser.cpp b/src/libclang/type_parser.cpp index 2f262a5..ffdc8af 100644 --- a/src/libclang/type_parser.cpp +++ b/src/libclang/type_parser.cpp @@ -7,9 +7,12 @@ #include #include #include +#include +#include #include #include -#include + +#include "libclang_visitor.hpp" using namespace cppast; @@ -171,6 +174,8 @@ namespace remove_prefix(spelling, "union"); auto entity = b(std::move(spelling)); + if (!entity) + return nullptr; return make_cv_qualified(std::move(entity), cv); } @@ -184,7 +189,7 @@ namespace } std::unique_ptr parse_type_impl(const detail::parse_context& context, - const CXType& type); + const CXCursor& cur, const CXType& type); std::unique_ptr parse_array_size(const CXType& type) { @@ -218,7 +223,7 @@ namespace } std::unique_ptr try_parse_array_type(const detail::parse_context& context, - const CXType& type) + const CXCursor& cur, const CXType& type) { auto canonical = clang_getCanonicalType(type); auto value_type = clang_getArrayElementType(type); @@ -236,18 +241,18 @@ namespace } auto size = parse_array_size(canonical); // type may not work, see above - return cpp_array_type::build(parse_type_impl(context, value_type), std::move(size)); + return cpp_array_type::build(parse_type_impl(context, cur, value_type), std::move(size)); } template std::unique_ptr add_parameters(Builder& builder, const detail::parse_context& context, - const CXType& type) + const CXCursor& cur, const CXType& type) { auto no_args = clang_getNumArgTypes(type); DEBUG_ASSERT(no_args >= 0, detail::parse_error_handler{}, type, "invalid number of arguments"); for (auto i = 0u; i != unsigned(no_args); ++i) - builder.add_parameter(parse_type_impl(context, clang_getArgType(type, i))); + builder.add_parameter(parse_type_impl(context, cur, clang_getArgType(type, i))); if (clang_isFunctionTypeVariadic(type)) builder.is_variadic(); @@ -256,15 +261,15 @@ namespace } std::unique_ptr try_parse_function_type(const detail::parse_context& context, - const CXType& type) + const CXCursor& cur, const CXType& type) { auto result = clang_getResultType(type); if (result.kind == CXType_Invalid) // not a function type return nullptr; - cpp_function_type::builder builder(parse_type_impl(context, result)); - return add_parameters(builder, context, type); + cpp_function_type::builder builder(parse_type_impl(context, cur, result)); + return add_parameters(builder, context, cur, type); } cpp_reference member_function_ref_qualifier(std::string& spelling) @@ -284,7 +289,7 @@ namespace } std::unique_ptr parse_member_pointee_type(const detail::parse_context& context, - const CXType& type) + const CXCursor& cur, const CXType& type) { auto spelling = get_type_spelling(type); auto ref = member_function_ref_qualifier(spelling); @@ -292,7 +297,7 @@ namespace auto class_t = clang_Type_getClassType(type); auto class_entity = - make_ref_qualified(make_cv_qualified(parse_type_impl(context, class_t), cv), ref); + make_ref_qualified(make_cv_qualified(parse_type_impl(context, cur, class_t), cv), ref); auto pointee = clang_getPointeeType(type); // for everything except the class type auto result = clang_getResultType(pointee); @@ -300,18 +305,227 @@ namespace { // member data pointer return cpp_member_object_type::build(std::move(class_entity), - parse_type_impl(context, pointee)); + parse_type_impl(context, cur, pointee)); } else { cpp_member_function_type::builder builder(std::move(class_entity), - parse_type_impl(context, result)); - return add_parameters(builder, context, pointee); + parse_type_impl(context, cur, result)); + return add_parameters(builder, context, cur, pointee); } } + bool is_direct_templated(const CXCursor& cur) + { + // TODO: variable template + auto kind = clang_getCursorKind(cur); + return kind == CXCursor_TypeAliasTemplateDecl || kind == CXCursor_ClassTemplate + || kind == CXCursor_ClassTemplatePartialSpecialization + || kind == CXCursor_FunctionTemplate; + } + + bool check_parent(const CXCursor& parent) + { + if (clang_Cursor_isNull(parent)) + return false; + auto kind = clang_getCursorKind(parent); + return kind != CXCursor_Namespace && kind != CXCursor_TranslationUnit; + } + + CXCursor get_template(CXCursor cur) + { + do + { + if (is_direct_templated(cur)) + return cur; + cur = clang_getCursorSemanticParent(cur); + } while (check_parent(cur)); + return clang_getNullCursor(); + } + + std::unique_ptr try_parse_template_parameter_type( + const detail::parse_context& context, const CXCursor& cur, const CXType& type) + { + // see if we have a parent template + auto templ = get_template(cur); + if (clang_Cursor_isNull(templ)) + // not a template + return nullptr; + + // doesn't respect cv qualifiers properly + auto result = + make_leave_type(type, [&](std::string&& type_spelling) -> std::unique_ptr { + // look at the template parameters, + // see if we find a matching one + auto param = clang_getNullCursor(); + detail::visit_children(templ, [&](const CXCursor& child) { + if (clang_getCursorKind(child) == CXCursor_TemplateTypeParameter + && get_type_spelling(clang_getCursorType(child)) == type_spelling) + { + // found one + DEBUG_ASSERT(clang_Cursor_isNull(param), detail::assert_handler{}); + param = child; + } + }); + + if (clang_Cursor_isNull(param)) + return nullptr; + else + // found matching parameter + return cpp_template_parameter_type::build( + cpp_template_type_parameter_ref(detail::get_entity_id(param), + std::move(type_spelling))); + }); + + if (result) + return result; + else + // try again in a possible parent template + return try_parse_template_parameter_type(context, clang_getCursorSemanticParent(templ), + type); + } + + const char* find_closing_bracket(const char* ptr) + { + for (auto paren_count = 0; *ptr; ++ptr) + { + if (*ptr == '(' || *ptr == '[' || *ptr == '{') + ++paren_count; + else if (*ptr == ')' || *ptr == ']' || *ptr == '}') + --paren_count; + else if (paren_count == 0 && *ptr == '>' && !std::isalnum(*ptr) && *ptr != '_') + // heuristic: this could be in fact a closing bracket + return ptr; + } + + return nullptr; + } + + std::string parse_argument(const CXCursor& cur, const char*& ptr, bool is_expression) + { + std::string arg; + + auto paren_count = 0; // non angle brackets + auto bracket_count = 0; // angle brackets + while (*ptr && ((paren_count + bracket_count) != 0 || *ptr != ',')) + { + if (*ptr == '(' || *ptr == '[' || *ptr == '{') + ++paren_count; + else if (*ptr == ')' || *ptr == ']' || *ptr == '}') + --paren_count; + // angle brackets are tricky + // as they can be both brackets and operators + // we only need to take care of those that aren't nested inside other brackets, luckily + else if (paren_count == 0) + { + if (is_expression && *ptr == '<') + { + // treat as brackets and see if it got a closing one + auto closing = find_closing_bracket(ptr); + if (closing) + { + // assume this is a closing bracket + while (ptr != closing) + arg += *ptr++; + } + } + // not an expression + // all top-level angle brackets are actually brackets + else if (*ptr == '<') + ++bracket_count; + else if (*ptr == '>') + --bracket_count; + } + + arg += *ptr++; + } + + DEBUG_ASSERT(*ptr == ',', detail::parse_error_handler{}, cur, + "unable to parse template argument"); + ++ptr; + + while (*ptr == ' ') + ++ptr; + while (!arg.empty() && arg.back() == ' ') + arg.pop_back(); + + return arg; + } + + CXCursor get_instantiation_template(const CXCursor& cur, const CXType& type, + const std::string& templ_name) + { + // look if the type has a declaration that is a template + auto decl = clang_getTypeDeclaration(type); + if (is_direct_templated(decl)) + return decl; + + // look if the templ_name matches a template template parameter + auto param = clang_getNullCursor(); + detail::visit_children(cur, [&](const CXCursor& child) { + if (clang_getCursorKind(child) == CXCursor_TemplateTemplateParameter + && detail::get_cursor_name(child) == templ_name.c_str()) + { + DEBUG_ASSERT(clang_Cursor_isNull(param), detail::parse_error_handler{}, cur, + "multiple template template parameters with the same name?!"); + param = child; + } + }); + return param; + } + + std::unique_ptr try_parse_instantiation_type(const detail::parse_context& context, + const CXCursor& cur, const CXType& type) + { + return make_leave_type(type, [&](std::string&& spelling) -> std::unique_ptr { + spelling.back() = ','; // to easily terminate the last argument + auto ptr = spelling.c_str(); + std::string templ_name; + for (; *ptr && *ptr != '<'; ++ptr) + templ_name += *ptr; + ++ptr; + + auto templ = get_instantiation_template(cur, type, templ_name); + if (clang_Cursor_isNull(templ)) + return nullptr; + + cpp_template_instantiation_type::builder builder( + cpp_template_ref(detail::get_entity_id(templ), std::move(templ_name))); + + // parse arguments + // visit children of declaration to get the kind of argument expected + // then parse the string + detail::visit_children(templ, [&](const CXCursor& child) { + if (!*ptr) + return; + + auto kind = clang_getCursorKind(child); + if (kind == CXCursor_TemplateTypeParameter) + { + auto arg = parse_argument(cur, ptr, false); + builder.add_argument( + std::unique_ptr(cpp_unexposed_type::build(std::move(arg)))); + } + else if (kind == CXCursor_NonTypeTemplateParameter) + { + auto arg = parse_argument(cur, ptr, true); + auto arg_type = detail::parse_type(context, child, clang_getCursorType(child)); + builder.add_argument(std::unique_ptr( + cpp_unexposed_expression::build(std::move(arg_type), std::move(arg)))); + } + else if (kind == CXCursor_TemplateTemplateParameter) + { + auto arg = parse_argument(cur, ptr, false); + builder.add_argument(cpp_template_ref(cpp_entity_id(""), std::move(arg))); + } + }); + + return builder.finish(); + }); + } + std::unique_ptr parse_type_impl(const detail::parse_context& context, - const CXType& type) + const CXCursor& cur, const CXType& type) { switch (type.kind) { @@ -335,13 +549,19 @@ namespace } // fallthrough case CXType_Unexposed: - if (auto ftype = try_parse_function_type(context, type)) + if (auto ftype = try_parse_function_type(context, cur, type)) // guess what: after you've called clang_getPointeeType() on a function pointer // you'll get an unexposed type return ftype; - else if (auto atype = try_parse_array_type(context, type)) + else if (auto atype = try_parse_array_type(context, cur, type)) // same deal here return atype; + else if (auto itype = try_parse_instantiation_type(context, cur, type)) + // instantiation unexposed + return itype; + else if (auto ptype = try_parse_template_parameter_type(context, cur, type)) + // template parameter type is unexposed + return ptype; return cpp_unexposed_type::build(get_type_spelling(type).c_str()); case CXType_Void: @@ -386,7 +606,7 @@ namespace case CXType_Pointer: { - auto pointee = parse_type_impl(context, clang_getPointeeType(type)); + auto pointee = parse_type_impl(context, cur, clang_getPointeeType(type)); auto pointer = cpp_pointer_type::build(std::move(pointee)); auto spelling = get_type_spelling(type); @@ -396,7 +616,7 @@ namespace case CXType_LValueReference: case CXType_RValueReference: { - auto referee = parse_type_impl(context, clang_getPointeeType(type)); + auto referee = parse_type_impl(context, cur, clang_getPointeeType(type)); return cpp_reference_type::build(std::move(referee), get_reference_kind(type)); } @@ -404,14 +624,14 @@ namespace case CXType_VariableArray: case CXType_DependentSizedArray: case CXType_ConstantArray: - return try_parse_array_type(context, type); + return try_parse_array_type(context, cur, type); case CXType_FunctionNoProto: case CXType_FunctionProto: - return try_parse_function_type(context, type); + return try_parse_function_type(context, cur, type); case CXType_MemberPointer: - return cpp_pointer_type::build(parse_member_pointee_type(context, type)); + return cpp_pointer_type::build(parse_member_pointee_type(context, cur, type)); // TODO: everything template related case CXType_Dependent: @@ -428,9 +648,9 @@ namespace } std::unique_ptr detail::parse_type(const detail::parse_context& context, - const CXType& type) + const CXCursor& cur, const CXType& type) { - auto result = parse_type_impl(context, type); + auto result = parse_type_impl(context, cur, type); DEBUG_ASSERT(result && is_valid(*result), detail::parse_error_handler{}, type, "invalid type"); return std::move(result); } @@ -465,15 +685,17 @@ std::unique_ptr detail::parse_raw_type(const detail::parse_context&, } std::unique_ptr detail::parse_cpp_type_alias(const detail::parse_context& context, - const CXCursor& cur, bool as_template) + const CXCursor& cur, + const CXCursor& template_cur) { 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 type = parse_type(context, clang_Cursor_isNull(template_cur) ? cur : template_cur, + clang_getTypedefDeclUnderlyingType(cur)); - if (as_template) + if (!clang_Cursor_isNull(template_cur)) return cpp_type_alias::build(name.c_str(), std::move(type)); else { diff --git a/src/libclang/variable_parser.cpp b/src/libclang/variable_parser.cpp index 74b7c61..0a51d81 100644 --- a/src/libclang/variable_parser.cpp +++ b/src/libclang/variable_parser.cpp @@ -36,7 +36,7 @@ std::unique_ptr detail::parse_cpp_variable(const detail::parse_conte DEBUG_ASSERT(cur.kind == CXCursor_VarDecl, detail::assert_handler{}); auto name = get_cursor_name(cur); - auto type = parse_type(context, clang_getCursorType(cur)); + auto type = parse_type(context, cur, clang_getCursorType(cur)); auto storage_class = get_storage_class(cur); auto is_constexpr = false; @@ -71,7 +71,7 @@ std::unique_ptr detail::parse_cpp_member_variable(const detail::pars DEBUG_ASSERT(cur.kind == CXCursor_FieldDecl, detail::assert_handler{}); auto name = get_cursor_name(cur); - auto type = parse_type(context, clang_getCursorType(cur)); + auto type = parse_type(context, cur, clang_getCursorType(cur)); auto is_mutable = clang_CXXField_isMutable(cur) != 0u; std::unique_ptr result; @@ -94,8 +94,9 @@ std::unique_ptr detail::parse_cpp_member_variable(const detail::pars // look for the equal sign, default value starts there while (!stream.done() && !skip_if(stream, "=")) stream.bump(); - auto default_value = parse_raw_expression(context, stream, stream.end(), - parse_type(context, clang_getCursorType(cur))); + auto default_value = + parse_raw_expression(context, stream, stream.end(), + parse_type(context, cur, clang_getCursorType(cur))); result = cpp_member_variable::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type), std::move(default_value), is_mutable); diff --git a/test/cpp_alias_template.cpp b/test/cpp_alias_template.cpp new file mode 100644 index 0000000..5390004 --- /dev/null +++ b/test/cpp_alias_template.cpp @@ -0,0 +1,136 @@ +// 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 "test_parser.hpp" + +using namespace cppast; + +TEST_CASE("cpp_alias_template") +{ + // no need to check advanced types here nor template parameters + auto code = R"( +template +using a = int; + +template +using b = T; + +template +using c = const T*; + +template +using d = a; + +template +using e = const b; + +template +using f = b{(0,1)}, int>; + +template class Templ> +using g = Templ; + +template +using h = g; +)"; + + auto check_parameters = + [](const cpp_alias_template& alias, + std::initializer_list> params) { + // no need to check more + auto cur = params.begin(); + for (auto& param : alias.parameters()) + { + REQUIRE(cur != params.end()); + REQUIRE(param.kind() == cur->first); + REQUIRE(param.name() == cur->second); + ++cur; + } + REQUIRE(cur == params.end()); + }; + + cpp_entity_index idx; + auto file = parse(idx, "cpp_alias_template.cpp", code); + auto count = test_visit(*file, [&](const cpp_alias_template& alias) { + if (alias.name() == "a") + { + check_parameters(alias, {{cpp_entity_kind::template_type_parameter_t, "T"}}); + REQUIRE(equal_types(idx, alias.type_alias().underlying_type(), + *cpp_builtin_type::build("int"))); + } + else if (alias.name() == "b") + { + check_parameters(alias, {{cpp_entity_kind::non_type_template_parameter_t, "I"}, + {cpp_entity_kind::template_type_parameter_t, "T"}}); + REQUIRE(equal_types(idx, alias.type_alias().underlying_type(), + *cpp_template_parameter_type::build( + cpp_template_type_parameter_ref(cpp_entity_id(""), "T")))); + } + else if (alias.name() == "c") + { + check_parameters(alias, {{cpp_entity_kind::template_type_parameter_t, "T"}}); + auto param = cpp_template_parameter_type::build( + cpp_template_type_parameter_ref(cpp_entity_id(""), "T")); + REQUIRE(equal_types(idx, alias.type_alias().underlying_type(), + *cpp_pointer_type::build( + cpp_cv_qualified_type::build(std::move(param), cpp_cv_const)))); + } + else if (alias.name() == "d") + { + check_parameters(alias, {{cpp_entity_kind::template_type_parameter_t, "T"}}); + + cpp_template_instantiation_type::builder builder( + cpp_template_ref(cpp_entity_id(""), "a")); + builder.add_argument(cpp_unexposed_type::build("void")); + REQUIRE(equal_types(idx, alias.type_alias().underlying_type(), *builder.finish())); + } + else if (alias.name() == "e") + { + check_parameters(alias, {{cpp_entity_kind::non_type_template_parameter_t, "I"}}); + + cpp_template_instantiation_type::builder builder( + cpp_template_ref(cpp_entity_id(""), "b")); + builder.add_argument( + cpp_unexposed_expression::build(cpp_builtin_type::build("int"), "I")); + REQUIRE(equal_types(idx, alias.type_alias().underlying_type(), + *cpp_cv_qualified_type::build(builder.finish(), cpp_cv_const))); + } + else if (alias.name() == "f") + { + check_parameters(alias, {{cpp_entity_kind::non_type_template_parameter_t, "I"}}); + + cpp_template_instantiation_type::builder builder( + cpp_template_ref(cpp_entity_id(""), "b")); + builder.add_argument(cpp_unexposed_expression::build(cpp_builtin_type::build("int"), + "I < a{(0 , 1)}")); + builder.add_argument(cpp_unexposed_type::build("int")); + REQUIRE(equal_types(idx, alias.type_alias().underlying_type(), *builder.finish())); + } + else if (alias.name() == "g") + { + check_parameters(alias, {{cpp_entity_kind::template_type_parameter_t, "T"}, + {cpp_entity_kind::template_template_parameter_t, "Templ"}}); + + cpp_template_instantiation_type::builder builder( + cpp_template_ref(cpp_entity_id(""), "Templ")); + builder.add_argument(cpp_unexposed_type::build("T")); + REQUIRE(equal_types(idx, alias.type_alias().underlying_type(), *builder.finish())); + } + else if (alias.name() == "h") + { + check_parameters(alias, {{cpp_entity_kind::template_type_parameter_t, "T"}}); + + cpp_template_instantiation_type::builder builder( + cpp_template_ref(cpp_entity_id(""), "g")); + builder.add_argument(cpp_unexposed_type::build("T")); + builder.add_argument(cpp_template_ref(cpp_entity_id("magic-allow-empty"), "a")); + REQUIRE(equal_types(idx, alias.type_alias().underlying_type(), *builder.finish())); + } + else + REQUIRE(false); + }); + REQUIRE(count == 8u); +} diff --git a/test/cpp_type_alias.cpp b/test/cpp_type_alias.cpp index 8950084..71ec930 100644 --- a/test/cpp_type_alias.cpp +++ b/test/cpp_type_alias.cpp @@ -6,11 +6,30 @@ #include #include +#include +#include #include "test_parser.hpp" using namespace cppast; +template +bool equal_ref(const cpp_entity_index& idx, const basic_cpp_entity_ref& parsed, + const basic_cpp_entity_ref& synthesized) +{ + if (parsed.name() != synthesized.name()) + return false; + else if (parsed.is_overloaded() != synthesized.is_overloaded()) + return false; + else if (parsed.is_overloaded()) + return false; + + auto entities = parsed.get(idx); + if (entities.size() != 1u) + return synthesized.id()[0u] == cpp_entity_id("magic-allow-empty"); + return entities[0u]->name().empty() || full_name(*entities[0u]) == parsed.name(); +} + bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_type& synthesized) { if (parsed.kind() != synthesized.kind()) @@ -24,15 +43,9 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_ case cpp_type_kind::user_defined: { - auto user_parsed = static_cast(parsed).entity(); - auto user_synthesized = static_cast(synthesized).entity(); - if (user_parsed.name() != user_synthesized.name()) - return false; - else if (user_parsed.is_overloaded()) - return false; - auto entities = user_parsed.get(idx); - REQUIRE(entities.size() == 1u); - return entities[0u]->name().empty() || full_name(*entities[0u]) == user_parsed.name(); + auto& user_parsed = static_cast(parsed); + auto& user_synthesized = static_cast(synthesized); + return equal_ref(idx, user_parsed.entity(), user_synthesized.entity()); } case cpp_type_kind::cv_qualified: @@ -69,16 +82,7 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_ auto& size_a = array_a.size().value(); auto& size_b = array_b.size().value(); - if (size_a.kind() != size_b.kind()) - return false; - else if (size_a.kind() == cpp_expression_kind::literal) - return static_cast(size_a).value() - == static_cast(size_b).value(); - else if (size_a.kind() == cpp_expression_kind::unexposed) - return static_cast(size_a).expression() - == static_cast(size_b).expression(); - else - break; + return equal_expressions(size_a, size_b); } case cpp_type_kind::function: @@ -92,7 +96,7 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_ return false; auto iter_a = func_a.parameter_types().begin(); - auto iter_b = func_a.parameter_types().begin(); + auto iter_b = func_b.parameter_types().begin(); while (iter_a != func_a.parameter_types().end() && iter_b != func_b.parameter_types().end()) { if (!equal_types(idx, *iter_a, *iter_b)) @@ -115,7 +119,7 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_ return false; auto iter_a = func_a.parameter_types().begin(); - auto iter_b = func_a.parameter_types().begin(); + auto iter_b = func_b.parameter_types().begin(); while (iter_a != func_a.parameter_types().end() && iter_b != func_b.parameter_types().end()) { if (!equal_types(idx, *iter_a, *iter_b)) @@ -135,11 +139,50 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_ return equal_types(idx, obj_a.object_type(), obj_b.object_type()); } - // TODO: implement equality when those can be parsed case cpp_type_kind::template_parameter: - break; + { + auto& entity_parsed = static_cast(parsed).entity(); + auto& entity_synthesized = + static_cast(synthesized).entity(); + return equal_ref(idx, entity_parsed, entity_synthesized); + } case cpp_type_kind::template_instantiation: - break; + { + auto& inst_parsed = static_cast(parsed); + auto& inst_synthesized = static_cast(synthesized); + + if (!equal_ref(idx, inst_parsed.primary_template(), inst_synthesized.primary_template())) + return false; + + auto iter_a = inst_parsed.arguments().begin(); + auto iter_b = inst_synthesized.arguments().begin(); + while (iter_a != inst_parsed.arguments().end() + && iter_b != inst_synthesized.arguments().end()) + { + if (iter_a->type().has_value() && iter_b->type().has_value()) + { + if (!equal_types(idx, iter_a->type().value(), iter_b->type().value())) + return false; + } + else if (iter_a->expression().has_value() && iter_b->expression().has_value()) + { + if (!equal_expressions(iter_a->expression().value(), iter_b->expression().value())) + return false; + } + else if (iter_a->template_ref().has_value() && iter_b->template_ref().has_value()) + { + if (!equal_ref(idx, iter_a->template_ref().value(), iter_b->template_ref().value())) + return false; + } + else + return false; + ++iter_a; + ++iter_b; + } + return iter_a == inst_parsed.arguments().end() + && iter_b == inst_synthesized.arguments().end(); + } + // TODO: implement equality when those can be parsed case cpp_type_kind::dependent: break;