cppast/test/cpp_function.cpp
2022-02-07 20:43:22 +01:00

381 lines
15 KiB
C++

// Copyright (C) 2017-2022 Jonathan Müller and cppast contributors
// SPDX-License-Identifier: MIT
#include <cppast/cpp_function.hpp>
#include <cppast/cpp_array_type.hpp>
#include <cppast/cpp_decltype_type.hpp>
#include "test_parser.hpp"
using namespace cppast;
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();
using m = int;
}
/// void ns::l();
void ns::l()
{
// might confuse parser
auto b = noexcept(g());
}
/// ns::m m();
ns::m m();
/// void n(int i=int());
void n(int i = int());
)";
auto check_body = [](const cpp_function& func, cpp_function_body_kind kind) {
REQUIRE(func.body_kind() == kind);
REQUIRE(func.is_declaration() == is_declaration(kind));
REQUIRE(func.is_definition() == is_definition(kind));
};
cpp_entity_index idx;
auto file = parse(idx, "cpp_function.cpp", code);
auto count = test_visit<cpp_function>(*file, [&](const cpp_function& func) {
if (func.name() == "a" || func.name() == "b" || func.name() == "c" || func.name() == "n")
{
REQUIRE(!func.noexcept_condition());
REQUIRE(func.storage_class() == cpp_storage_class_none);
REQUIRE(!func.is_constexpr());
check_body(func, cpp_function_declaration);
if (func.name() == "a")
{
REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build(cpp_void)));
REQUIRE(func.signature() == "()");
REQUIRE(!func.is_variadic());
}
else if (func.name() == "b")
{
REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build(cpp_int)));
REQUIRE(func.signature() == "(int,float*)");
auto count = 0u;
for (auto& param : func.parameters())
{
if (param.name() == "a")
{
REQUIRE(equal_types(idx, param.type(), *cpp_builtin_type::build(cpp_int)));
REQUIRE(!param.default_value());
}
else if (param.name() == "b")
{
REQUIRE(equal_types(idx, param.type(),
*cpp_pointer_type::build(
cpp_builtin_type::build(cpp_float))));
REQUIRE(param.default_value());
REQUIRE(
equal_expressions(param.default_value().value(),
*cpp_unexposed_expression::
build(cpp_pointer_type::build(
cpp_builtin_type::build(cpp_float)),
cpp_token_string::tokenize("nullptr"))));
}
else
REQUIRE(false);
++count;
}
REQUIRE(count == 2u);
REQUIRE(!func.is_variadic());
}
else if (func.name() == "c")
{
REQUIRE(
equal_types(idx, func.return_type(),
*cpp_reference_type::
build(cpp_array_type::build(cpp_builtin_type::build(cpp_int),
cpp_literal_expression::
build(cpp_builtin_type::build(
cpp_ulonglong),
"10")),
cpp_ref_lvalue)));
REQUIRE(func.signature() == "(decltype(42),...)");
auto count = 0u;
for (auto& param : func.parameters())
{
if (param.name() == "a")
{
REQUIRE(equal_types(idx, param.type(),
*cpp_decltype_type::build(
cpp_unexposed_expression::
build(cpp_builtin_type::build(cpp_int),
cpp_token_string::tokenize("42")))));
REQUIRE(!param.default_value());
}
else
REQUIRE(false);
++count;
}
REQUIRE(count == 1u);
REQUIRE(func.is_variadic());
}
else if (func.name() == "n")
{
REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build(cpp_void)));
REQUIRE(func.signature() == "(int)");
auto count = 0u;
for (auto& param : func.parameters())
{
if (param.name() == "i")
{
REQUIRE(equal_types(idx, param.type(), *cpp_builtin_type::build(cpp_int)));
REQUIRE(param.default_value());
REQUIRE(equal_expressions(param.default_value().value(),
*cpp_unexposed_expression::
build(cpp_pointer_type::build(
cpp_builtin_type::build(cpp_int)),
cpp_token_string::tokenize("int()"))));
}
else
REQUIRE(false);
++count;
}
REQUIRE(count == 1u);
REQUIRE(!func.is_variadic());
}
}
else if (func.name() == "d" || func.name() == "e" || func.name() == "f")
{
REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build(cpp_void)));
REQUIRE(func.signature() == "()");
REQUIRE(!func.is_variadic());
REQUIRE(func.storage_class() == cpp_storage_class_none);
REQUIRE(!func.is_constexpr());
REQUIRE(func.noexcept_condition());
check_body(func, cpp_function_declaration);
auto bool_t = cpp_builtin_type::build(cpp_bool);
if (func.name() == "d")
REQUIRE(
equal_expressions(func.noexcept_condition().value(),
*cpp_literal_expression::build(std::move(bool_t), "true")));
else if (func.name() == "e")
REQUIRE(
equal_expressions(func.noexcept_condition().value(),
*cpp_unexposed_expression::build(std::move(bool_t),
cpp_token_string::tokenize(
"false"))));
else if (func.name() == "f")
REQUIRE(
equal_expressions(func.noexcept_condition().value(),
*cpp_unexposed_expression::build(std::move(bool_t),
cpp_token_string::tokenize(
"noexcept(d())"))));
}
else if (func.name() == "g" || func.name() == "h" || func.name() == "i"
|| func.name() == "j")
{
REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build(cpp_void)));
REQUIRE(func.signature() == "()");
REQUIRE(!func.is_variadic());
REQUIRE(!func.noexcept_condition());
check_body(func, cpp_function_declaration);
if (func.name() == "g")
{
REQUIRE(!func.is_constexpr());
REQUIRE(func.storage_class() == cpp_storage_class_extern);
}
else if (func.name() == "h")
{
REQUIRE(!func.is_constexpr());
REQUIRE(func.storage_class() == cpp_storage_class_static);
}
else if (func.name() == "i")
{
REQUIRE(func.is_constexpr());
REQUIRE(func.storage_class() == cpp_storage_class_none);
}
else if (func.name() == "j")
{
REQUIRE(func.is_constexpr());
REQUIRE(func.storage_class() == cpp_storage_class_static);
}
}
else if (func.name() == "k" || func.name() == "l")
{
REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build(cpp_void)));
REQUIRE(func.signature() == "()");
REQUIRE(!func.is_variadic());
REQUIRE(!func.noexcept_condition());
REQUIRE(!func.is_constexpr());
REQUIRE(func.storage_class() == cpp_storage_class_none);
if (func.name() == "k")
check_body(func, cpp_function_deleted);
else if (func.name() == "l")
{
if (func.semantic_scope() == "ns::")
check_body(func, cpp_function_definition);
else
check_body(func, cpp_function_declaration);
}
}
else if (func.name() == "m")
{
REQUIRE(equal_types(idx, func.return_type(),
*cpp_user_defined_type::build(
cpp_type_ref(cpp_entity_id(""), "ns::m"))));
REQUIRE(count_children(func.parameters()) == 0u);
REQUIRE(!func.is_variadic());
REQUIRE(!func.noexcept_condition());
REQUIRE(!func.is_constexpr());
REQUIRE(func.storage_class() == cpp_storage_class_none);
check_body(func, cpp_function_declaration);
}
else
REQUIRE(false);
});
REQUIRE(count == 15u);
}
TEST_CASE("consteval cpp_function")
{
if (libclang_parser::libclang_minor_version() < 60)
return;
auto code = R"(
/// consteval void p();
consteval void p();
/// static consteval void q();
static consteval void q();
)";
auto check_body = [](const cpp_function& func, cpp_function_body_kind kind) {
REQUIRE(func.body_kind() == kind);
REQUIRE(func.is_declaration() == is_declaration(kind));
REQUIRE(func.is_definition() == is_definition(kind));
};
cpp_entity_index idx;
auto file = parse(idx, "consteval_function.cpp", code, false, cppast::cpp_standard::cpp_2a);
auto count = test_visit<cpp_function>(*file, [&](const cpp_function& func) {
if (func.name() == "p" || func.name() == "q")
{
REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build(cpp_void)));
REQUIRE(func.signature() == "()");
REQUIRE(!func.is_variadic());
REQUIRE(!func.noexcept_condition());
check_body(func, cpp_function_declaration);
if (func.name() == "p")
{
REQUIRE(func.is_consteval());
REQUIRE(func.storage_class() == cpp_storage_class_none);
}
else if (func.name() == "q")
{
REQUIRE(func.is_consteval());
REQUIRE(func.storage_class() == cpp_storage_class_static);
}
}
else
REQUIRE(false);
});
REQUIRE(count == 2u);
}
TEST_CASE("static cpp_function")
{
auto code = R"(
// 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() {}
)";
cpp_entity_index idx;
auto file = parse(idx, "static_cpp_function.cpp", code);
auto count = test_visit<cpp_function>(*file, [&](const cpp_function& func) {
REQUIRE(!func.is_variadic());
REQUIRE(func.signature() == "()");
REQUIRE(func.storage_class() == cpp_storage_class_static);
if (func.name() == "a")
{
REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build(cpp_void)));
REQUIRE(!func.noexcept_condition());
REQUIRE(!func.is_constexpr());
if (func.semantic_scope() == "foo::")
REQUIRE(func.body_kind() == cpp_function_definition);
else
REQUIRE(func.body_kind() == cpp_function_declaration);
}
else if (func.name() == "b")
{
REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build(cpp_int)));
REQUIRE(func.noexcept_condition());
REQUIRE(
equal_expressions(func.noexcept_condition().value(),
*cpp_literal_expression::build(cpp_builtin_type::build(cpp_bool),
"true")));
REQUIRE(!func.is_constexpr());
REQUIRE(func.body_kind() == cpp_function_definition);
}
else if (func.name() == "c")
{
REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build(cpp_char)));
REQUIRE(!func.noexcept_condition());
REQUIRE(func.is_constexpr());
REQUIRE(func.body_kind() == cpp_function_deleted);
}
else
REQUIRE(false);
});
REQUIRE(count == 4u);
}