Parse cpp_alias_template

This commit is contained in:
Jonathan Müller 2017-03-24 16:49:31 +01:00
commit d6f0997fb6
14 changed files with 550 additions and 82 deletions

View file

@ -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

View file

@ -164,8 +164,8 @@ namespace cppast
class cpp_template;
/// A reference to a [cppast::cpp_template]().
using cpp_template_ref = basic_cpp_entity_ref<cpp_template, detail::cpp_template_ref_predicate>;
/// A reference to a [cppast::cpp_template]() or a [cppast::cpp_template_template_parameter]().
using cpp_template_ref = basic_cpp_entity_ref<cpp_entity, detail::cpp_template_ref_predicate>;
/// 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 <typename T,
typename std::enable_if<std::is_base_of<cpp_type, T>::value, int>::type = 0>
cpp_template_argument(std::unique_ptr<T> type)
: arg_(std::unique_ptr<cpp_type>(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<std::is_base_of<cpp_expression, T>::value>::type>
cpp_template_argument(std::unique_ptr<T> expr)
: arg_(std::unique_ptr<cpp_expression>(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<const cpp_type> type() const noexcept
{
return arg_.optional_value(type_safe::variant_type<std::unique_ptr<cpp_type>>{})
.map([](const std::unique_ptr<cpp_type>& type) { return type_safe::ref(*type); });
}
type_safe::optional_ref<const cpp_expression> expression() const noexcept
{
return arg_.optional_value(type_safe::variant_type<std::unique_ptr<cpp_expression>>{})
.map([](const std::unique_ptr<cpp_expression>& expr) {
return type_safe::ref(*expr);
});
}
type_safe::optional_ref<const cpp_template_ref> template_ref() const noexcept
{
return arg_.optional_value(type_safe::variant_type<cpp_template_ref>{});
}
private:
type_safe::variant<std::unique_ptr<cpp_type>, std::unique_ptr<cpp_expression>,
cpp_template_ref>;
cpp_template_ref>
arg_;
};
} // namespace cppast
#endif // CPPAST_CPP_TEMPLATE_PARAMETER_HPP_INCLUDED

View file

@ -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

View file

@ -56,7 +56,7 @@ namespace
std::unique_ptr<cpp_type> 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));
}

View file

@ -29,7 +29,7 @@ std::unique_ptr<cpp_expression> 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() != ')'))
{

View file

@ -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<cpp_expression> 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<cpp_entity> 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<cpp_entity> 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);

View file

@ -68,7 +68,8 @@ 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, 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<cpp_entity> 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:

View file

@ -58,7 +58,8 @@ namespace cppast
comment_context comments;
};
std::unique_ptr<cpp_type> parse_type(const parse_context& context, const CXType& type);
std::unique_ptr<cpp_type> parse_type(const parse_context& context, const 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<cpp_entity> parse_cpp_type_alias(const parse_context& context,
const CXCursor& cur, bool as_template);
const CXCursor& cur,
const CXCursor& template_cur);
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,
@ -127,8 +129,9 @@ namespace cppast
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);
std::unique_ptr<cpp_entity> parse_entity(
const parse_context& context, const CXCursor& cur,
const CXCursor& template_cur = clang_getNullCursor());
}
} // namespace cppast::detail

View file

@ -12,8 +12,8 @@ using namespace cppast;
namespace
{
template <typename TemplateT, typename EntityT>
typename TemplateT::builder get_builder(const detail::parse_context& context,
const CXCursor& cur)
type_safe::optional<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
@ -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<cpp_entity> detail::parse_cpp_alias_template(const detail::parse
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));
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));
}

View file

@ -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();

View file

@ -7,9 +7,12 @@
#include <cppast/cpp_array_type.hpp>
#include <cppast/cpp_expression.hpp>
#include <cppast/cpp_function_type.hpp>
#include <cppast/cpp_template.hpp>
#include <cppast/cpp_template_parameter.hpp>
#include <cppast/cpp_type.hpp>
#include <cppast/cpp_type_alias.hpp>
#include <clang-c/Index.h>
#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<cpp_type> parse_type_impl(const detail::parse_context& context,
const CXType& type);
const CXCursor& cur, const CXType& type);
std::unique_ptr<cpp_expression> parse_array_size(const CXType& type)
{
@ -218,7 +223,7 @@ namespace
}
std::unique_ptr<cpp_type> 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 <class Builder>
std::unique_ptr<cpp_type> 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<cpp_type> 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<cpp_type> 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<cpp_type> 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<cpp_type> {
// 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<cpp_type> 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<cpp_type> {
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_type>(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_expression>(
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<cpp_type> 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<cpp_type> 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<cpp_type> detail::parse_raw_type(const detail::parse_context&,
}
std::unique_ptr<cpp_entity> 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
{

View file

@ -36,7 +36,7 @@ std::unique_ptr<cpp_entity> 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<cpp_entity> 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<cpp_member_variable_base> result;
@ -94,8 +94,9 @@ std::unique_ptr<cpp_entity> 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);

136
test/cpp_alias_template.cpp Normal file
View file

@ -0,0 +1,136 @@
// 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 "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 <typename T>
using a = int;
template <int I, typename T = void>
using b = T;
template <typename T>
using c = const T*;
template <typename T>
using d = a<void>;
template <int I>
using e = const b<I>;
template <int I>
using f = b<I < a<int>{(0,1)}, int>;
template <typename T, template <typename> class Templ>
using g = Templ<T>;
template <typename T>
using h = g<T, a>;
)";
auto check_parameters =
[](const cpp_alias_template& alias,
std::initializer_list<std::pair<cpp_entity_kind, const char*>> 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<cpp_alias_template>(*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<int>{(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);
}

View file

@ -6,11 +6,30 @@
#include <cppast/cpp_array_type.hpp>
#include <cppast/cpp_function_type.hpp>
#include <cppast/cpp_template.hpp>
#include <cppast/cpp_template_parameter.hpp>
#include "test_parser.hpp"
using namespace cppast;
template <typename T, class Predicate>
bool equal_ref(const cpp_entity_index& idx, const basic_cpp_entity_ref<T, Predicate>& parsed,
const basic_cpp_entity_ref<T, Predicate>& 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<const cpp_user_defined_type&>(parsed).entity();
auto user_synthesized = static_cast<const cpp_user_defined_type&>(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<const cpp_user_defined_type&>(parsed);
auto& user_synthesized = static_cast<const cpp_user_defined_type&>(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<const cpp_literal_expression&>(size_a).value()
== static_cast<const cpp_literal_expression&>(size_b).value();
else if (size_a.kind() == cpp_expression_kind::unexposed)
return static_cast<const cpp_unexposed_expression&>(size_a).expression()
== static_cast<const cpp_unexposed_expression&>(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<const cpp_template_parameter_type&>(parsed).entity();
auto& entity_synthesized =
static_cast<const cpp_template_parameter_type&>(synthesized).entity();
return equal_ref(idx, entity_parsed, entity_synthesized);
}
case cpp_type_kind::template_instantiation:
break;
{
auto& inst_parsed = static_cast<const cpp_template_instantiation_type&>(parsed);
auto& inst_synthesized = static_cast<const cpp_template_instantiation_type&>(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;