Implement code generation

This commit is contained in:
Jonathan Müller 2017-03-29 19:51:02 +02:00
commit d18070a799
29 changed files with 2522 additions and 280 deletions

39
test/code_generator.cpp Normal file
View file

@ -0,0 +1,39 @@
// 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/code_generator.hpp>
#include "test_parser.hpp"
TEST_CASE("code_generator")
{
// no need to check much here, as each entity check separately
// only write some file with equivalent code and synopsis
auto code = R"(using type=int;
struct foo{
int a;
auto func(int)->int(*)[];
private:
int const b=42;
};
int(*(foo::* mptr)(int))[];
enum class bar
:int{
a,
b=42
};
void func(int(*)(int));
extern void(* ptr)(int(*)(int))=&func;
)";
auto file = parse({}, "code_generator.cpp", code);
REQUIRE(get_code(*file) == code);
}

View file

@ -12,27 +12,43 @@ 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 <typename T>
using a = int;
/// template<int I,typename T=void>
/// using b=T;
template <int I, typename T = void>
using b = T;
/// template<typename T>
/// using c=T const*;
template <typename T>
using c = const T*;
/// template<typename T>
/// using d=a<void>;
template <typename T>
using d = a<void>;
/// template<int I>
/// using e=b<I> const;
template <int I>
using e = const b<I>;
/// template<int I>
/// using f=b<I < a<int>{(0 , 1)}, int>;
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, template <typename> class Templ>
using g = Templ<T>;
/// template<typename T>
/// using h=g<T, a>;
template <typename T>
using h = g<T, a>;
)";

View file

@ -12,16 +12,40 @@ TEST_CASE("cpp_class")
{
auto code = R"(
// forward declarations
/// struct a;
struct a;
/// class b;
class b;
/// struct unresolved;
struct unresolved;
// basic
/// struct a{
/// };
struct a {};
/// class b final{
/// };
class b final {};
/// union c{
/// };
union c {};
// members
/// struct d{
/// enum m1{
/// };
///
/// enum m2{
/// };
///
/// private:
/// enum m3{
/// };
///
/// protected:
/// enum m4{
/// };
/// };
struct d
{
enum m1 {};
@ -38,20 +62,31 @@ protected:
};
// bases
/// class e
/// :a,d{
/// };
class e
: a, private d {};
namespace ns
{
/// struct base{
/// };
struct base {};
}
/// struct f
/// :ns::base,virtual protected e{
/// };
struct f
: public ns::base, virtual protected e
{};
using namespace ns;
/// struct g
/// :base{
/// };
struct g
: base {};
)";

View file

@ -16,20 +16,45 @@ TEST_CASE("cpp_class_template")
{
auto code = R"(
// check everything not related to members first
/// template<typename T>
/// class a{
/// };
template <typename T>
class a {};
/// template<int I,typename T>
/// struct b{
/// };
template <int I, typename T>
struct b {};
/// template<template<typename>class T>
/// union c;
template <template <typename> class T>
union c;
// bases
/// template<typename T>
/// struct d
/// :T,a<T>,T::type,a<T>::type{
/// };
template <typename T>
struct d : T, a<T>, T::type, a<T>::type {};
// members
/// template<typename T>
/// struct e{
/// T var_a;
///
/// a<T> var_b;
///
/// typename T::type var_c;
///
/// typename a<T>::type var_d;
///
/// template<typename U>
/// T func(U);
/// };
template <typename T>
struct e
{
@ -43,16 +68,28 @@ struct e
};
// full specialization
/// template<>
/// class a<int>{
/// };
template <>
class a<int> {};
/// template<>
/// struct b<0,int>{
/// };
template <>
struct b<0, int> {};
// partial specialization
/// template<typename T>
/// class a<T*>{
/// };
template <typename T>
class a<T*> {};
/// template<typename T>
/// struct b<0,T>{
/// };
template <typename T>
struct b<0, T> {};
)";

View file

@ -11,6 +11,12 @@ using namespace cppast;
TEST_CASE("cpp_enum")
{
auto code = R"(
/// enum a{
/// a_a,
/// a_b=42,
/// a_c,
/// a_d=a_a+2
/// };
enum a
{
a_a,
@ -19,8 +25,15 @@ enum a
a_d = a_a + 2,
};
/// enum class b;
enum class b; // forward declaration
/// enum class b
/// :int{
/// b_a,
/// b_b=42,
/// b_c
/// };
enum class b : int
{
b_a,
@ -28,6 +41,8 @@ enum class b : int
b_c
};
/// enum c
/// :int;
enum c : int;
)";
@ -54,7 +69,7 @@ enum c : int;
++no_vals;
REQUIRE(val.value());
auto& expr = val.value().value();
REQUIRE(expr.kind() == cpp_expression_kind::unexposed);
REQUIRE(expr.kind() == cpp_expression_kind::unexposed_t);
REQUIRE(static_cast<const cpp_unexposed_expression&>(expr).expression()
== "42");
REQUIRE(equal_types(idx, expr.type(), *cpp_builtin_type::build(cpp_uint)));
@ -65,7 +80,7 @@ enum c : int;
REQUIRE(val.value());
auto& expr = val.value().value();
// this is unexposed for some reason
REQUIRE(expr.kind() == cpp_expression_kind::unexposed);
REQUIRE(expr.kind() == cpp_expression_kind::unexposed_t);
REQUIRE(static_cast<const cpp_unexposed_expression&>(expr).expression()
== "a_a+2");
REQUIRE(equal_types(idx, expr.type(), *cpp_builtin_type::build(cpp_uint)));
@ -99,7 +114,7 @@ enum c : int;
++no_vals;
REQUIRE(val.value());
auto& expr = val.value().value();
REQUIRE(expr.kind() == cpp_expression_kind::literal);
REQUIRE(expr.kind() == cpp_expression_kind::literal_t);
REQUIRE(static_cast<const cpp_literal_expression&>(expr).value() == "42");
REQUIRE(equal_types(idx, expr.type(), *cpp_builtin_type::build(cpp_int)));
}

View file

@ -15,29 +15,42 @@ TEST_CASE("cpp_function")
{
auto code = R"(
// parameters and return type are only tested here
/// void a();
void a();
/// int b(int a,float* b=nullptr);
int b(int a, float* b = nullptr);
/// auto c(decltype(42) a,...)->int(&)[10];
int (&c(decltype(42) a, ...))[10];
// noexcept conditions
/// void d()noexcept;
void d() noexcept;
/// void e()noexcept(false);
void e() noexcept(false);
/// void f()noexcept(noexcept(d()));
void f() noexcept(noexcept(d()));
// storage class + constexpr
/// extern void g();
extern void g();
/// static void h();
static void h();
/// constexpr void i();
constexpr void i();
/// static constexpr void j();
static constexpr void j();
// body
namespace ns
{
/// void k()=delete;
void k() = delete;
/// void l();
void l();
}
/// void ns::l();
void ns::l()
{
// might confuse parser
@ -215,13 +228,17 @@ TEST_CASE("static cpp_function")
// no need to test anything special
struct foo
{
/// static void a();
static void a();
/// static int b()noexcept;
static int b() noexcept { return 0; }
/// static constexpr char c()=delete;
static constexpr char c() = delete;
};
/// static void foo::a();
void foo::a() {}
)";

View file

@ -17,36 +17,56 @@ TEST_CASE("cpp_function_template")
template <int I>
using type = int;
/// template<typename T>
/// T a(T const& t);
template <typename T>
T a(const T& t);
struct d
{
/// template<int I,typename T>
/// static type<I> b(T);
template <int I, typename T>
static type<I> b(T);
/// template<typename T=const int>
/// T c();
template <typename T = const int>
auto c() -> T;
/// template<typename T>
/// operator T()const;
template <typename T>
operator T() const;
/// template<typename T>
/// d(T const&);
template <typename T>
d(const T&);
};
/// template<>
/// int a(int const& t);
template <>
int a(const int& t);
/// template<>
/// static type<0> d::b<0,int>(int);
template <>
type<0> d::b<0, int>(int);
/// template<>
/// int d::c();
template <>
auto d::c() -> int;
/// template<>
/// d::operator int()const;
template <>
d::operator int() const;
/// template<>
/// d::d(int const&);
template <>
d::d(const int&);
)";

View file

@ -13,10 +13,22 @@ using namespace cppast;
TEST_CASE("cpp_language_linkage")
{
auto code = R"(
/// extern "C" enum a{
/// };
extern "C" enum a {};
enum b {};
/// extern "C++"{
/// enum c{
/// };
///
/// enum d{
/// };
///
/// enum e{
/// };
/// }
extern "C++" // yup
{
enum c {};
@ -41,21 +53,23 @@ enum f {};
REQUIRE(count == 2u);
// check enums for their correct parent
count = test_visit<cpp_enum>(*file, [&](const cpp_enum& e) {
if (e.name() == "a")
check_parent(e, "\"C\"", "a");
else if (e.name() == "b")
check_parent(e, "cpp_language_linkage.cpp", "b");
else if (e.name() == "c")
check_parent(e, "\"C++\"", "c");
else if (e.name() == "d")
check_parent(e, "\"C++\"", "d");
else if (e.name() == "e")
check_parent(e, "\"C++\"", "e");
else if (e.name() == "f")
check_parent(e, "cpp_language_linkage.cpp", "f");
else
REQUIRE(false);
});
count = test_visit<cpp_enum>(*file,
[&](const cpp_enum& e) {
if (e.name() == "a")
check_parent(e, "\"C\"", "a");
else if (e.name() == "b")
check_parent(e, "cpp_language_linkage.cpp", "b");
else if (e.name() == "c")
check_parent(e, "\"C++\"", "c");
else if (e.name() == "d")
check_parent(e, "\"C++\"", "d");
else if (e.name() == "e")
check_parent(e, "\"C++\"", "e");
else if (e.name() == "f")
check_parent(e, "cpp_language_linkage.cpp", "f");
else
REQUIRE(false);
},
false); // don't check code generation here
REQUIRE(count == 6u);
}

View file

@ -14,24 +14,36 @@ TEST_CASE("cpp_member_function")
// no need to test parameters/return types
struct foo
{
/// void a();
void a();
/// void b()noexcept;
void b() noexcept;
/// void c()const;
void c() const;
auto d() const volatile -> void;
/// void d()const volatile noexcept;
auto d() const volatile noexcept -> void;
/// void e()&;
void e() &;
/// void f()const volatile&&;
void f() const volatile &&;
/// virtual void g();
virtual void g();
/// virtual void h()=0;
virtual void h() = 0;
/// void i();
void i() {}
/// void j()=delete;
void j() = delete;
};
struct bar : foo
{
/// virtual void g() override;
void g();
/// virtual void h() override final;
virtual auto h() -> void override final;
};
)";
@ -43,7 +55,7 @@ struct bar : foo
REQUIRE(!func.is_variadic());
REQUIRE(!func.is_constexpr());
REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build(cpp_void)));
if (func.name() != "b")
if (func.name() != "b" && func.name() != "d")
REQUIRE(!func.noexcept_condition());
if (func.name() != "g" && func.name() != "h")
REQUIRE(!func.virtual_info());
@ -142,8 +154,11 @@ namespace ns
// most of it only need to be check in member function
struct foo
{
/// operator int&();
operator int&();
/// explicit operator bool()const;
explicit operator bool() const;
/// constexpr operator ns::type();
constexpr operator ns::type();
};
)";
@ -192,8 +207,11 @@ TEST_CASE("cpp_constructor")
// only test constructor specific stuff
struct foo
{
/// foo()noexcept=default;
foo() noexcept = default;
/// explicit foo(int);
explicit foo(int);
/// constexpr foo(int,char)=delete;
constexpr foo(int, char) = delete;
};
)";
@ -240,21 +258,25 @@ TEST_CASE("cpp_destructor")
auto code = R"(
struct a
{
/// ~a();
~a();
};
struct b
{
/// ~b()noexcept(false);
~b() noexcept(false) {}
};
struct c
{
/// virtual ~c()=default;
virtual ~c() = default;
};
struct d : c
{
/// virtual ~d() override final;
~d() final;
};
)";

View file

@ -13,8 +13,11 @@ TEST_CASE("cpp_member_variable")
auto code = R"(
struct foo
{
/// int a;
int a;
/// float b=3.14f;
float b = 3.14f;
/// mutable char c;
mutable char c;
};
)";
@ -59,10 +62,15 @@ TEST_CASE("cpp_bitfield")
auto code = R"(
struct foo
{
/// char a:3;
char a : 3;
/// mutable char b:2;
mutable char b : 2;
/// char:0;
char : 0;
/// char c:3;
char c : 3;
/// char:4;
char : 4;
};
)";

View file

@ -11,12 +11,22 @@ using namespace cppast;
TEST_CASE("cpp_namespace")
{
auto code = R"(
/// namespace a{
/// }
namespace a {}
/// inline namespace b{
/// }
inline namespace b {}
/// namespace c{
/// namespace d{
/// }
/// }
namespace c
{
/// namespace d{
/// }
namespace d {}
}
)";
@ -57,18 +67,24 @@ TEST_CASE("cpp_namespace_alias")
namespace outer {}
namespace ns {}
/// namespace a=outer;
namespace a = outer;
/// namespace b=ns;
namespace b = ns;
namespace outer
{
namespace ns {}
/// namespace c=ns;
namespace c = ns;
/// namespace d=::outer;
namespace d = ::outer;
}
/// namespace e=outer::ns;
namespace e = outer::ns;
/// namespace f=outer::c;
namespace f = outer::c;
)";
@ -117,17 +133,22 @@ TEST_CASE("cpp_using_directive")
namespace ns1 {}
namespace ns2 {}
/// using namespace ns1;
using namespace ns1;
/// using namespace ns2;
using namespace ns2;
namespace outer
{
namespace ns {}
/// using namespace ns;
using namespace ns;
/// using namespace ::ns1;
using namespace ::ns1;
}
/// using namespace outer::ns;
using namespace outer::ns;
)";
@ -180,7 +201,9 @@ namespace ns2
enum b {};
}
/// using ns1::a;
using ns1::a;
/// using ns2::b;
using ns2::b;
namespace outer
@ -190,10 +213,13 @@ namespace outer
enum c {};
}
/// using ns::c;
using ns::c;
}
/// using outer::ns::c;
using outer::ns::c;
/// using outer::c;
using outer::c;
namespace ns1
@ -202,6 +228,7 @@ namespace ns1
void d(float);
}
/// using ns1::d;
using ns1::d;
)";

View file

@ -13,15 +13,21 @@ TEST_CASE("cpp_macro_definition")
auto code = R"(
#include <iostream>
#define G
/// #define A
#define A
/// #define B hello
#define B hello
namespace ns {}
/// #define C(x,y) x##_name
#define C(x, y) x##_name
/// #define D(...) __VA_ARGS__
#define D(...) __VA_ARGS__
/// #define E() barbaz
#define E() bar\
baz
namespace ns2
{
/// #define F () bar
#define F () bar
#undef G
}

View file

@ -35,60 +35,82 @@ using e = void;
cpp_entity_index idx;
auto file = parse(idx, "cpp_template_type_parameter.cpp", code);
auto count = test_visit<cpp_alias_template>(*file, [&](const cpp_alias_template& alias) {
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(),
*cpp_builtin_type::build(cpp_void)));
auto count =
test_visit<cpp_alias_template>(*file,
[&](const cpp_alias_template& alias) {
REQUIRE(equal_types(idx,
alias.type_alias().underlying_type(),
*cpp_builtin_type::build(cpp_void)));
for (auto& p : alias.parameters())
{
REQUIRE(p.kind() == cpp_entity_kind::template_type_parameter_t);
for (auto& p : alias.parameters())
{
REQUIRE(
p.kind()
== cpp_entity_kind::template_type_parameter_t);
auto& param = static_cast<const cpp_template_type_parameter&>(p);
if (param.name() == "A")
{
REQUIRE(alias.name() == "a");
REQUIRE(param.keyword() == cpp_template_keyword::keyword_typename);
REQUIRE(!param.is_variadic());
REQUIRE(!param.default_type());
}
else if (param.name() == "B")
{
REQUIRE(alias.name() == "b");
REQUIRE(param.keyword() == cpp_template_keyword::keyword_class);
REQUIRE(param.is_variadic());
REQUIRE(!param.default_type());
}
else if (param.name() == "")
{
REQUIRE(alias.name() == "c");
REQUIRE(param.keyword() == cpp_template_keyword::keyword_typename);
REQUIRE(!param.is_variadic());
REQUIRE(param.default_type().has_value());
REQUIRE(equal_types(idx, param.default_type().value(),
*cpp_unexposed_type::build("const int*")));
}
else if (param.name() == "D")
{
REQUIRE(alias.name() == "d");
REQUIRE(param.keyword() == cpp_template_keyword::keyword_class);
REQUIRE(!param.is_variadic());
REQUIRE(param.default_type().has_value());
REQUIRE(equal_types(idx, param.default_type().value(),
*cpp_unexposed_type::build("decltype(1+3)")));
}
else if (param.name() == "E")
{
REQUIRE(alias.name() == "e");
REQUIRE(param.keyword() == cpp_template_keyword::keyword_typename);
REQUIRE(!param.is_variadic());
REQUIRE(param.default_type().has_value());
REQUIRE(equal_types(idx, param.default_type().value(),
*cpp_unexposed_type::build("a<void>")));
}
else
REQUIRE(false);
}
});
auto& param =
static_cast<const cpp_template_type_parameter&>(
p);
if (param.name() == "A")
{
REQUIRE(alias.name() == "a");
REQUIRE(
param.keyword()
== cpp_template_keyword::keyword_typename);
REQUIRE(!param.is_variadic());
REQUIRE(!param.default_type());
}
else if (param.name() == "B")
{
REQUIRE(alias.name() == "b");
REQUIRE(param.keyword()
== cpp_template_keyword::keyword_class);
REQUIRE(param.is_variadic());
REQUIRE(!param.default_type());
}
else if (param.name() == "")
{
REQUIRE(alias.name() == "c");
REQUIRE(
param.keyword()
== cpp_template_keyword::keyword_typename);
REQUIRE(!param.is_variadic());
REQUIRE(param.default_type().has_value());
REQUIRE(equal_types(idx,
param.default_type().value(),
*cpp_unexposed_type::build(
"const int*")));
}
else if (param.name() == "D")
{
REQUIRE(alias.name() == "d");
REQUIRE(param.keyword()
== cpp_template_keyword::keyword_class);
REQUIRE(!param.is_variadic());
REQUIRE(param.default_type().has_value());
REQUIRE(equal_types(idx,
param.default_type().value(),
*cpp_unexposed_type::build(
"decltype(1+3)")));
}
else if (param.name() == "E")
{
REQUIRE(alias.name() == "e");
REQUIRE(
param.keyword()
== cpp_template_keyword::keyword_typename);
REQUIRE(!param.is_variadic());
REQUIRE(param.default_type().has_value());
REQUIRE(equal_types(idx,
param.default_type().value(),
*cpp_unexposed_type::build(
"a<void>")));
}
else
REQUIRE(false);
}
},
false); // can't check synopsis with comments
REQUIRE(count == 5u);
}
@ -109,57 +131,62 @@ using d = void;
)";
cpp_entity_index idx;
auto file = parse(idx, "cpp_non_type_template_parameter.cpp", code);
auto count = test_visit<cpp_alias_template>(*file, [&](const cpp_alias_template& alias) {
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(),
*cpp_builtin_type::build(cpp_void)));
auto file = parse(idx, "cpp_non_type_template_parameter.cpp", code);
auto count = test_visit<cpp_alias_template>(
*file,
[&](const cpp_alias_template& alias) {
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(),
*cpp_builtin_type::build(cpp_void)));
for (auto& p : alias.parameters())
{
REQUIRE(p.kind() == cpp_entity_kind::non_type_template_parameter_t);
for (auto& p : alias.parameters())
{
REQUIRE(p.kind() == cpp_entity_kind::non_type_template_parameter_t);
auto& param = static_cast<const cpp_non_type_template_parameter&>(p);
if (param.name() == "A")
{
REQUIRE(alias.name() == "a");
REQUIRE(equal_types(idx, param.type(), *cpp_builtin_type::build(cpp_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(cpp_char))));
REQUIRE(!param.is_variadic());
REQUIRE(param.default_value());
REQUIRE(equal_expressions(param.default_value().value(),
auto& param = static_cast<const cpp_non_type_template_parameter&>(p);
if (param.name() == "A")
{
REQUIRE(alias.name() == "a");
REQUIRE(equal_types(idx, param.type(), *cpp_builtin_type::build(cpp_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(cpp_char))));
REQUIRE(!param.is_variadic());
REQUIRE(param.default_value());
REQUIRE(
equal_expressions(param.default_value().value(),
*cpp_literal_expression::build(cpp_builtin_type::build(
cpp_nullptr),
"nullptr")));
}
else if (param.name() == "C")
{
REQUIRE(alias.name() == "c");
REQUIRE(equal_types(idx, param.type(), *cpp_builtin_type::build(cpp_int)));
REQUIRE(param.is_variadic());
REQUIRE(!param.default_value());
}
else if (param.name() == "D")
{
REQUIRE(alias.name() == "d");
}
else if (param.name() == "C")
{
REQUIRE(alias.name() == "c");
REQUIRE(equal_types(idx, param.type(), *cpp_builtin_type::build(cpp_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(cpp_void));
builder.is_variadic();
REQUIRE(equal_types(idx, param.type(), *cpp_pointer_type::build(builder.finish())));
cpp_function_type::builder builder(cpp_builtin_type::build(cpp_void));
builder.is_variadic();
REQUIRE(
equal_types(idx, param.type(), *cpp_pointer_type::build(builder.finish())));
REQUIRE(!param.is_variadic());
REQUIRE(!param.default_value());
REQUIRE(!param.is_variadic());
REQUIRE(!param.default_value());
}
else
REQUIRE(false);
}
else
REQUIRE(false);
}
});
},
false); // can't check synopsis with comments
REQUIRE(count == 4u);
}
@ -187,105 +214,108 @@ using d = void;
)";
cpp_entity_index idx;
auto file = parse(idx, "cpp_template_template_parameter.cpp", code);
auto count = test_visit<cpp_alias_template>(*file, [&](const cpp_alias_template& alias) {
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(),
*cpp_builtin_type::build(cpp_void)));
if (alias.name() == "def")
return;
auto file = parse(idx, "cpp_template_template_parameter.cpp", code);
auto count = test_visit<cpp_alias_template>(
*file,
[&](const cpp_alias_template& alias) {
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(),
*cpp_builtin_type::build(cpp_void)));
if (alias.name() == "def")
return;
for (auto& p : alias.parameters())
{
REQUIRE(p.kind() == cpp_entity_kind::template_template_parameter_t);
auto& param = static_cast<const cpp_template_template_parameter&>(p);
REQUIRE(param.keyword() == cpp_template_keyword::keyword_class);
if (param.name() == "A")
for (auto& p : alias.parameters())
{
REQUIRE(alias.name() == "a");
REQUIRE(!param.is_variadic());
REQUIRE(!param.default_template());
REQUIRE(p.kind() == cpp_entity_kind::template_template_parameter_t);
auto no = 0u;
for (auto& p_param : param)
auto& param = static_cast<const cpp_template_template_parameter&>(p);
REQUIRE(param.keyword() == cpp_template_keyword::keyword_class);
if (param.name() == "A")
{
++no;
REQUIRE(p_param.name() == "T");
REQUIRE(p_param.kind() == cpp_entity_kind::template_type_parameter_t);
}
REQUIRE(no == 1u);
}
else if (param.name() == "B")
{
REQUIRE(alias.name() == "b");
REQUIRE(param.is_variadic());
REQUIRE(!param.default_template());
REQUIRE(alias.name() == "a");
REQUIRE(!param.is_variadic());
REQUIRE(!param.default_template());
auto cur = param.begin();
REQUIRE(cur != param.end());
REQUIRE(cur->name().empty());
REQUIRE(cur->kind() == cpp_entity_kind::non_type_template_parameter_t);
++cur;
REQUIRE(cur != param.end());
REQUIRE(cur->name().empty());
REQUIRE(cur->kind() == cpp_entity_kind::template_type_parameter_t);
++cur;
REQUIRE(cur == param.end());
}
else if (param.name() == "C")
{
REQUIRE(alias.name() == "c");
REQUIRE(!param.is_variadic());
REQUIRE(param.default_template());
auto def = param.default_template().value();
REQUIRE(def.name() == "ns::def");
auto entities = def.get(idx);
REQUIRE(entities.size() == 1u);
REQUIRE(entities[0]->name() == "def");
auto no = 0u;
for (auto& p_param : param)
{
++no;
REQUIRE(p_param.name() == "");
REQUIRE(p_param.kind() == cpp_entity_kind::non_type_template_parameter_t);
}
REQUIRE(no == 1u);
}
else if (param.name() == "D")
{
REQUIRE(alias.name() == "d");
REQUIRE(!param.is_variadic());
REQUIRE(param.default_template());
auto def = param.default_template().value();
REQUIRE(def.name() == "a");
auto entities = def.get(idx);
REQUIRE(entities.size() == 1u);
REQUIRE(entities[0]->name() == "a");
auto no = 0u;
for (auto& p_param : param)
{
++no;
REQUIRE(p_param.name() == "");
REQUIRE(p_param.kind() == cpp_entity_kind::template_template_parameter_t);
for (auto& p_p_param :
static_cast<const cpp_template_template_parameter&>(p_param))
auto no = 0u;
for (auto& p_param : param)
{
++no;
REQUIRE(p_p_param.name() == "");
REQUIRE(p_p_param.kind() == cpp_entity_kind::template_type_parameter_t);
REQUIRE(p_param.name() == "T");
REQUIRE(p_param.kind() == cpp_entity_kind::template_type_parameter_t);
}
REQUIRE(no == 1u);
}
REQUIRE(no == 2u);
else if (param.name() == "B")
{
REQUIRE(alias.name() == "b");
REQUIRE(param.is_variadic());
REQUIRE(!param.default_template());
auto cur = param.begin();
REQUIRE(cur != param.end());
REQUIRE(cur->name().empty());
REQUIRE(cur->kind() == cpp_entity_kind::non_type_template_parameter_t);
++cur;
REQUIRE(cur != param.end());
REQUIRE(cur->name().empty());
REQUIRE(cur->kind() == cpp_entity_kind::template_type_parameter_t);
++cur;
REQUIRE(cur == param.end());
}
else if (param.name() == "C")
{
REQUIRE(alias.name() == "c");
REQUIRE(!param.is_variadic());
REQUIRE(param.default_template());
auto def = param.default_template().value();
REQUIRE(def.name() == "ns::def");
auto entities = def.get(idx);
REQUIRE(entities.size() == 1u);
REQUIRE(entities[0]->name() == "def");
auto no = 0u;
for (auto& p_param : param)
{
++no;
REQUIRE(p_param.name() == "");
REQUIRE(p_param.kind() == cpp_entity_kind::non_type_template_parameter_t);
}
REQUIRE(no == 1u);
}
else if (param.name() == "D")
{
REQUIRE(alias.name() == "d");
REQUIRE(!param.is_variadic());
REQUIRE(param.default_template());
auto def = param.default_template().value();
REQUIRE(def.name() == "a");
auto entities = def.get(idx);
REQUIRE(entities.size() == 1u);
REQUIRE(entities[0]->name() == "a");
auto no = 0u;
for (auto& p_param : param)
{
++no;
REQUIRE(p_param.name() == "");
REQUIRE(p_param.kind() == cpp_entity_kind::template_template_parameter_t);
for (auto& p_p_param :
static_cast<const cpp_template_template_parameter&>(p_param))
{
++no;
REQUIRE(p_p_param.name() == "");
REQUIRE(p_p_param.kind() == cpp_entity_kind::template_type_parameter_t);
}
}
REQUIRE(no == 2u);
}
else
REQUIRE(false);
}
else
REQUIRE(false);
}
});
},
false); // can't check synopsis with comments
REQUIRE(count == 5u);
}

View file

@ -21,26 +21,26 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
switch (parsed.kind())
{
case cpp_type_kind::builtin:
case cpp_type_kind::builtin_t:
return static_cast<const cpp_builtin_type&>(parsed).builtin_type_kind()
== static_cast<const cpp_builtin_type&>(synthesized).builtin_type_kind();
case cpp_type_kind::user_defined:
case cpp_type_kind::user_defined_t:
{
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::auto_:
case cpp_type_kind::auto_t:
return true;
case cpp_type_kind::decltype_:
case cpp_type_kind::decltype_t:
return equal_expressions(static_cast<const cpp_decltype_type&>(parsed).expression(),
static_cast<const cpp_decltype_type&>(synthesized).expression());
case cpp_type_kind::decltype_auto:
case cpp_type_kind::decltype_auto_t:
return true;
case cpp_type_kind::cv_qualified:
case cpp_type_kind::cv_qualified_t:
{
auto& cv_a = static_cast<const cpp_cv_qualified_type&>(parsed);
auto& cv_b = static_cast<const cpp_cv_qualified_type&>(synthesized);
@ -48,10 +48,10 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
&& equal_types(idx, cv_a.type(), cv_b.type());
}
case cpp_type_kind::pointer:
case cpp_type_kind::pointer_t:
return equal_types(idx, static_cast<const cpp_pointer_type&>(parsed).pointee(),
static_cast<const cpp_pointer_type&>(synthesized).pointee());
case cpp_type_kind::reference:
case cpp_type_kind::reference_t:
{
auto& ref_a = static_cast<const cpp_reference_type&>(parsed);
auto& ref_b = static_cast<const cpp_reference_type&>(synthesized);
@ -59,7 +59,7 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
&& equal_types(idx, ref_a.referee(), ref_b.referee());
}
case cpp_type_kind::array:
case cpp_type_kind::array_t:
{
auto& array_a = static_cast<const cpp_array_type&>(parsed);
auto& array_b = static_cast<const cpp_array_type&>(synthesized);
@ -77,7 +77,7 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
return equal_expressions(size_a, size_b);
}
case cpp_type_kind::function:
case cpp_type_kind::function_t:
{
auto& func_a = static_cast<const cpp_function_type&>(parsed);
auto& func_b = static_cast<const cpp_function_type&>(synthesized);
@ -98,7 +98,7 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
}
return iter_a == func_a.parameter_types().end() && iter_b == func_b.parameter_types().end();
}
case cpp_type_kind::member_function:
case cpp_type_kind::member_function_t:
{
auto& func_a = static_cast<const cpp_member_function_type&>(parsed);
auto& func_b = static_cast<const cpp_member_function_type&>(synthesized);
@ -121,7 +121,7 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
}
return iter_a == func_a.parameter_types().end() && iter_b == func_b.parameter_types().end();
}
case cpp_type_kind::member_object:
case cpp_type_kind::member_object_t:
{
auto& obj_a = static_cast<const cpp_member_object_type&>(parsed);
auto& obj_b = static_cast<const cpp_member_object_type&>(synthesized);
@ -131,14 +131,14 @@ 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());
}
case cpp_type_kind::template_parameter:
case cpp_type_kind::template_parameter_t:
{
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:
case cpp_type_kind::template_instantiation_t:
{
auto& inst_parsed = static_cast<const cpp_template_instantiation_type&>(parsed);
auto& inst_synthesized = static_cast<const cpp_template_instantiation_type&>(synthesized);
@ -179,10 +179,10 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
&& iter_b == inst_synthesized.arguments().end();
}
// TODO: implement equality when those can be parsed
case cpp_type_kind::dependent:
case cpp_type_kind::dependent_t:
break;
case cpp_type_kind::unexposed:
case cpp_type_kind::unexposed_t:
return static_cast<const cpp_unexposed_type&>(parsed).name()
== static_cast<const cpp_unexposed_type&>(synthesized).name();
}
@ -194,45 +194,66 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
// other test cases don't need that anymore
TEST_CASE("cpp_type_alias")
{
const char* code = nullptr;
const char* code = nullptr;
auto check_code = false;
SECTION("using")
{
code = R"(
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
@ -241,12 +262,14 @@ using u = const struct u_ {}*;
using v = struct {};
// decltype
/// using w=decltype(0);
using w = decltype(0);
)";
}
SECTION("typedef")
{
code = R"(
check_code = false; // will always generate using
code = R"(
// basic
typedef int a;
typedef const long double volatile b;
@ -455,6 +478,8 @@ typedef decltype(0) w;
{
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")
{
@ -462,11 +487,13 @@ typedef decltype(0) w;
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(""), "v"));
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
return false;
}
else if (alias.name() == "w")
{
@ -476,6 +503,8 @@ typedef decltype(0) w;
}
else
REQUIRE(false);
return check_code;
});
REQUIRE(count == 23u);
}

View file

@ -14,17 +14,25 @@ TEST_CASE("cpp_variable")
{
auto code = R"(
// basic
/// int a;
int a;
/// unsigned long long b=42;
unsigned long long b = 42;
/// float c=3.f+0.14f;
float c = 3.f + 0.14f;
// with storage class specifiers
/// extern int d;
extern int d; // actually declaration
/// static int e;
static int e;
/// thread_local int f;
thread_local int f;
/// static thread_local int g;
thread_local static int g;
// constexpr
/// constexpr int const h=12;
constexpr int h = 12;
// inline definition
@ -34,11 +42,15 @@ struct baz {} k{};
static struct {} l;
// auto
/// auto m=128;
auto m = 128;
/// auto const& n=m;
const auto& n = m;
// decltype
/// decltype(0) o;
decltype(0) o;
/// decltype(o) const& p=o;
const decltype(o)& p = o;
)";
@ -106,10 +118,14 @@ const decltype(o)& p = o;
"12")),
cpp_storage_class_none, true, false);
else if (var.name() == "i")
{
check_variable(var,
*cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "foo")),
nullptr, cpp_storage_class_none, false, false);
return false; // can't check code here
}
else if (var.name() == "j")
{
check_variable(var, *cpp_cv_qualified_type::build(cpp_user_defined_type::build(
cpp_type_ref(cpp_entity_id(""),
"bar")),
@ -120,7 +136,10 @@ const decltype(o)& p = o;
"bar")),
"bar()")),
cpp_storage_class_none, false, false);
return false;
}
else if (var.name() == "k")
{
check_variable(var,
*cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "baz")),
type_safe::ref(
@ -129,9 +148,14 @@ const decltype(o)& p = o;
"baz")),
"{}")),
cpp_storage_class_none, false, false);
return false;
}
else if (var.name() == "l")
{
check_variable(var, *cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "")),
nullptr, cpp_storage_class_static, false, false);
return false;
}
else if (var.name() == "m")
check_variable(var, *cpp_auto_type::build(),
type_safe::ref(
@ -167,6 +191,8 @@ const decltype(o)& p = o;
cpp_storage_class_none, false, false);
else
REQUIRE(false);
return true;
});
REQUIRE(count == 16u);
}
@ -176,8 +202,11 @@ TEST_CASE("static cpp_variable")
auto code = R"(
struct test
{
/// static int a;
static int a;
/// static int const b;
static const int b;
/// static thread_local int c;
static thread_local int c;
};
)";

View file

@ -9,6 +9,7 @@
#include <catch.hpp>
#include <cppast/code_generator.hpp>
#include <cppast/cpp_entity_kind.hpp>
#include <cppast/cpp_expression.hpp>
#include <cppast/cpp_type.hpp>
@ -39,8 +40,72 @@ inline std::unique_ptr<cppast::cpp_file> parse(const cppast::cpp_entity_index& i
return result;
}
class test_generator : public cppast::code_generator
{
public:
const std::string& str() const noexcept
{
return str_;
}
private:
void do_indent() override
{
++indent_;
}
void do_unindent() override
{
if (indent_)
--indent_;
}
void do_write_token_seq(cppast::string_view tokens)
{
if (was_newline_)
{
str_ += std::string(indent_ * 2u, ' ');
was_newline_ = false;
}
str_ += tokens.c_str();
}
void do_write_newline() override
{
str_ += "\n";
was_newline_ = true;
}
std::string str_;
unsigned indent_ = 0;
bool was_newline_ = false;
};
inline std::string get_code(const cppast::cpp_entity& e)
{
test_generator generator;
cppast::generate_code(generator, e);
auto str = generator.str();
if (!str.empty() && str.back() == '\n')
str.pop_back();
return str;
}
template <typename Func, typename T>
auto visit_callback(bool, Func f, const T& t) -> decltype(f(t) == true)
{
return f(t);
}
template <typename Func, typename T>
bool visit_callback(int check, Func f, const T& t)
{
f(t);
return check == 1;
}
template <typename T, typename Func>
unsigned test_visit(const cppast::cpp_file& file, Func f)
unsigned test_visit(const cppast::cpp_file& file, Func f, bool check_code = true)
{
auto count = 0u;
cppast::visit(file, [&](const cppast::cpp_entity& e, cppast::visitor_info info) {
@ -49,9 +114,16 @@ unsigned test_visit(const cppast::cpp_file& file, Func f)
if (e.kind() == T::kind())
{
auto& obj = static_cast<const T&>(e);
f(obj);
auto& obj = static_cast<const T&>(e);
auto check_cur = visit_callback(check_code, f, obj);
++count;
if (check_cur)
{
INFO(e.name());
REQUIRE(e.comment());
REQUIRE(e.comment().value() == get_code(e));
}
}
return true;
@ -88,11 +160,11 @@ inline bool equal_expressions(const cppast::cpp_expression& parsed,
return false;
switch (parsed.kind())
{
case cpp_expression_kind::unexposed:
case cpp_expression_kind::unexposed_t:
return static_cast<const cpp_unexposed_expression&>(parsed).expression()
== static_cast<const cpp_unexposed_expression&>(synthesized).expression();
case cpp_expression_kind::literal:
case cpp_expression_kind::literal_t:
return static_cast<const cpp_literal_expression&>(parsed).value()
== static_cast<const cpp_literal_expression&>(synthesized).value();
}