// Copyright (C) 2017-2019 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
#include
#include
#include "test_parser.hpp"
using namespace cppast;
bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_type& synthesized)
{
if (parsed.kind() != synthesized.kind())
return false;
switch (parsed.kind())
{
case cpp_type_kind::builtin_t:
return static_cast(parsed).builtin_type_kind()
== static_cast(synthesized).builtin_type_kind();
case cpp_type_kind::user_defined_t:
{
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::auto_t:
return true;
case cpp_type_kind::decltype_t:
return equal_expressions(static_cast(parsed).expression(),
static_cast(synthesized).expression());
case cpp_type_kind::decltype_auto_t:
return true;
case cpp_type_kind::cv_qualified_t:
{
auto& cv_a = static_cast(parsed);
auto& cv_b = static_cast(synthesized);
return cv_a.cv_qualifier() == cv_b.cv_qualifier()
&& equal_types(idx, cv_a.type(), cv_b.type());
}
case cpp_type_kind::pointer_t:
return equal_types(idx, static_cast(parsed).pointee(),
static_cast(synthesized).pointee());
case cpp_type_kind::reference_t:
{
auto& ref_a = static_cast(parsed);
auto& ref_b = static_cast(synthesized);
return ref_a.reference_kind() == ref_b.reference_kind()
&& equal_types(idx, ref_a.referee(), ref_b.referee());
}
case cpp_type_kind::array_t:
{
auto& array_a = static_cast(parsed);
auto& array_b = static_cast(synthesized);
// check value type
if (!equal_types(idx, array_a.value_type(), array_b.value_type()))
return false;
// check size
if (!array_a.size().has_value() && !array_b.size().has_value())
return true;
auto& size_a = array_a.size().value();
auto& size_b = array_b.size().value();
return equal_expressions(size_a, size_b);
}
case cpp_type_kind::function_t:
{
auto& func_a = static_cast(parsed);
auto& func_b = static_cast(synthesized);
if (!equal_types(idx, func_a.return_type(), func_b.return_type()))
return false;
else if (func_a.is_variadic() != func_b.is_variadic())
return false;
auto iter_a = 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))
return false;
++iter_a;
++iter_b;
}
return iter_a == func_a.parameter_types().end() && iter_b == func_b.parameter_types().end();
}
case cpp_type_kind::member_function_t:
{
auto& func_a = static_cast(parsed);
auto& func_b = static_cast(synthesized);
if (!equal_types(idx, func_a.return_type(), func_b.return_type()))
return false;
else if (!equal_types(idx, func_a.class_type(), func_b.class_type()))
return false;
else if (func_a.is_variadic() != func_b.is_variadic())
return false;
auto iter_a = 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))
return false;
++iter_a;
++iter_b;
}
return iter_a == func_a.parameter_types().end() && iter_b == func_b.parameter_types().end();
}
case cpp_type_kind::member_object_t:
{
auto& obj_a = static_cast(parsed);
auto& obj_b = static_cast(synthesized);
if (!equal_types(idx, obj_a.class_type(), obj_b.class_type()))
return false;
return equal_types(idx, obj_a.object_type(), obj_b.object_type());
}
case cpp_type_kind::template_parameter_t:
{
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_t:
{
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;
else if (inst_parsed.arguments_exposed() != inst_synthesized.arguments_exposed())
return false;
else if (!inst_parsed.arguments_exposed())
return inst_parsed.unexposed_arguments() == inst_synthesized.unexposed_arguments();
else if (inst_parsed.arguments().has_value() != inst_synthesized.arguments().has_value())
return false;
else if (!inst_parsed.arguments().has_value())
return true;
auto iter_a = inst_parsed.arguments().value().begin();
auto iter_b = inst_synthesized.arguments().value().begin();
while (iter_a != inst_parsed.arguments().value().end()
&& iter_b != inst_synthesized.arguments().value().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().value().end()
&& iter_b == inst_synthesized.arguments().value().end();
}
case cpp_type_kind::dependent_t:
{
auto& dep_a = static_cast(parsed);
auto& dep_b = static_cast(synthesized);
return dep_a.name() == dep_b.name() && equal_types(idx, dep_a.dependee(), dep_b.dependee());
}
case cpp_type_kind::unexposed_t:
return static_cast(parsed).name()
== static_cast(synthesized).name();
}
return false;
}
// also tests the type parsing code
// other test cases don't need that anymore
TEST_CASE("cpp_type_alias")
{
const char* code = nullptr;
auto check_code = false;
SECTION("using")
{
check_code = true;
code = R"(
// basic
/// using a=int;
using a = int;
/// using b=long double const volatile;
using b = const long double volatile;
// pointers
/// using c=int*;
using c = int*;
/// using d=unsigned int const*;
using d = const unsigned int*;
/// using e=unsigned int const* volatile;
using e = unsigned const * volatile;
// references
/// using f=int&;
using f = int&;
/// using g=int const&&;
using g = const int&&;
// user-defined types
/// using h=c;
using h = c;
/// using i=d const;
using i = const d;
/// using j=e*;
using j = e*;
// arrays
/// using k=int[42];
using k = int[42];
/// using l=float*[];
using l = float*[];
/// using m=char[42];
using m = char[3 * 2 + 4 ? 42 : 43];
// function pointers
/// using n=void(*)(int);
using n = void(*)(int);
/// using o=char*(&)(int const&,...);
using o = char*(&)(const int&,...);
/// using p=n(*)(int,o);
using p = n(*)(int, o);
struct foo {};
// member function pointers
/// using q=void(foo::*)(int);
using q = void(foo::*)(int);
/// using r=void(foo::*)(int,...)const&;
using r = void(foo::*)(int,...) const &;
// member data pointers
/// using s=int(foo::*);
using s = int(foo::*);
// user-defined types inline definition
using t = struct t_ {};
using u = const struct u_ {}*;
using v = struct {};
// decltype
/// using w=decltype(0);
using w = decltype(0);
)";
}
SECTION("typedef")
{
check_code = false; // will always generate using
code = R"(
// basic
typedef int a;
typedef const long double volatile b;
// pointers
typedef int* c;
typedef const unsigned int* d;
typedef unsigned const * volatile e;
// references
typedef int& f;
typedef const int&& g;
// user-defined types
typedef c h;
typedef const d i;
typedef e* j;
// arrays
typedef int k[42];
typedef float* l[];
typedef char m[3 * 2 + 4 ? 42 : 43];
// function pointers
typedef void(*n)(int);
typedef char*(&o)(const int&,...);
typedef n(*p)(int, o);
struct foo {};
// member function pointers
typedef void(foo::*q)(int);
typedef void(foo::*r)(int,...) const &;
// member data pointers
typedef int(foo::*s);
// user-defined types inline definition
typedef struct t_ {} t;
typedef const struct u_ {}* u;
typedef struct {} v;
// decltype
typedef decltype(0) w;
)";
}
auto add_cv = [](std::unique_ptr type, cpp_cv cv) {
return cpp_cv_qualified_type::build(std::move(type), cv);
};
auto make_size = [](std::string size, bool literal) -> std::unique_ptr {
auto type = cpp_builtin_type::build(cpp_ulonglong);
if (literal)
return cpp_literal_expression::build(std::move(type), std::move(size));
else
return cpp_unexposed_expression::build(std::move(type),
cpp_token_string::tokenize(std::move(size)));
};
cpp_entity_index idx;
auto file = parse(idx, "cpp_type_alias.cpp", code);
auto count = test_visit(*file, [&](const cpp_type_alias& alias) {
if (alias.name() == "a")
{
auto type = cpp_builtin_type::build(cpp_int);
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
}
else if (alias.name() == "b")
{
auto type = add_cv(cpp_builtin_type::build(cpp_longdouble), cpp_cv_const_volatile);
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
}
else if (alias.name() == "c")
{
auto type = cpp_pointer_type::build(cpp_builtin_type::build(cpp_int));
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
}
else if (alias.name() == "d")
{
auto type
= cpp_pointer_type::build(add_cv(cpp_builtin_type::build(cpp_uint), cpp_cv_const));
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
}
else if (alias.name() == "e")
{
auto type = add_cv(cpp_pointer_type::build(
add_cv(cpp_builtin_type::build(cpp_uint), cpp_cv_const)),
cpp_cv_volatile);
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
}
else if (alias.name() == "f")
{
auto type = cpp_reference_type::build(cpp_builtin_type::build(cpp_int), cpp_ref_lvalue);
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
}
else if (alias.name() == "g")
{
auto type
= cpp_reference_type::build(add_cv(cpp_builtin_type::build(cpp_int), cpp_cv_const),
cpp_ref_rvalue);
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
}
else if (alias.name() == "h")
{
auto type = cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "c"));
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
}
else if (alias.name() == "i")
{
auto type = add_cv(cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "d")),
cpp_cv_const);
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
}
else if (alias.name() == "j")
{
auto type = cpp_pointer_type::build(
cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "e")));
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
}
else if (alias.name() == "k")
{
auto type
= cpp_array_type::build(cpp_builtin_type::build(cpp_int), make_size("42", true));
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
}
else if (alias.name() == "l")
{
auto type
= cpp_array_type::build(cpp_pointer_type::build(cpp_builtin_type::build(cpp_float)),
nullptr);
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
}
else if (alias.name() == "m")
{
auto type
= cpp_array_type::build(cpp_builtin_type::build(cpp_char), make_size("42", true));
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
}
else if (alias.name() == "n")
{
cpp_function_type::builder builder(cpp_builtin_type::build(cpp_void));
builder.add_parameter(cpp_builtin_type::build(cpp_int));
auto type = cpp_pointer_type::build(builder.finish());
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
}
else if (alias.name() == "o")
{
cpp_function_type::builder builder(
cpp_pointer_type::build(cpp_builtin_type::build(cpp_char)));
builder.add_parameter(
cpp_reference_type::build(add_cv(cpp_builtin_type::build(cpp_int), cpp_cv_const),
cpp_ref_lvalue));
builder.is_variadic();
auto type = cpp_reference_type::build(builder.finish(), cpp_ref_lvalue);
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
}
else if (alias.name() == "p")
{
cpp_function_type::builder builder(
cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "n")));
builder.add_parameter(cpp_builtin_type::build(cpp_int));
builder.add_parameter(
cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "o")));
auto type = cpp_pointer_type::build(builder.finish());
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
}
else if (alias.name() == "q")
{
cpp_member_function_type::builder builder(cpp_user_defined_type::build(
cpp_type_ref(cpp_entity_id(""), "foo")),
cpp_builtin_type::build(cpp_void));
builder.add_parameter(cpp_builtin_type::build(cpp_int));
auto type = cpp_pointer_type::build(builder.finish());
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
}
else if (alias.name() == "r")
{
auto obj = cpp_reference_type::build(add_cv(cpp_user_defined_type::build(
cpp_type_ref(cpp_entity_id(""), "foo")),
cpp_cv_const),
cpp_ref_lvalue);
cpp_member_function_type::builder builder(std::move(obj),
cpp_builtin_type::build(cpp_void));
builder.add_parameter(cpp_builtin_type::build(cpp_int));
builder.is_variadic();
auto type = cpp_pointer_type::build(builder.finish());
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
}
else if (alias.name() == "s")
{
// in old libclang version, the member type isn't exposed for some reason
auto pointee
= cpp_member_object_type::build(cpp_user_defined_type::build(
cpp_type_ref(cpp_entity_id(""), "foo")),
cpp_unexposed_type::build("int"));
auto type = cpp_pointer_type::build(std::move(pointee));
auto is_unexposed = equal_types(idx, alias.underlying_type(), *type);
if (!is_unexposed)
{
pointee = cpp_member_object_type::build(cpp_user_defined_type::build(
cpp_type_ref(cpp_entity_id(""), "foo")),
cpp_builtin_type::build(cpp_int));
type = cpp_pointer_type::build(std::move(pointee));
auto is_builtin = equal_types(idx, alias.underlying_type(), *type);
REQUIRE(is_builtin);
}
}
else if (alias.name() == "t")
{
auto type = cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "t_"));
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
return false; // inhibit comment check for next three entities
// as they can't be documented (will always apply to the inline type)
}
else if (alias.name() == "u")
{
auto type = cpp_pointer_type::build(
add_cv(cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "u_")),
cpp_cv_const));
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
return false;
}
else if (alias.name() == "v")
{
auto type = cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), ""));
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
return false;
}
else if (alias.name() == "w")
{
auto type = cpp_decltype_type::build(
cpp_unexposed_expression::build(cpp_builtin_type::build(cpp_int),
cpp_token_string::tokenize("0")));
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
}
else
REQUIRE(false);
return check_code;
});
REQUIRE(count == 23u);
}