Parse free functions
This commit is contained in:
parent
210fcf2c36
commit
9267bbbff2
15 changed files with 442 additions and 58 deletions
|
|
@ -17,6 +17,8 @@ namespace cppast
|
|||
class cpp_function_parameter final : public cpp_entity, public cpp_variable_base
|
||||
{
|
||||
public:
|
||||
static cpp_entity_kind kind() noexcept;
|
||||
|
||||
/// \returns A newly created and registered function parameter.
|
||||
static std::unique_ptr<cpp_function_parameter> build(
|
||||
const cpp_entity_index& idx, cpp_entity_id id, std::string name,
|
||||
|
|
@ -121,6 +123,7 @@ namespace cppast
|
|||
std::unique_ptr<T> finish(const cpp_entity_index& idx, cpp_entity_id id,
|
||||
cpp_function_body_kind body_kind)
|
||||
{
|
||||
function->body_ = body_kind;
|
||||
if (cppast::is_definition(body_kind))
|
||||
idx.register_entity(std::move(id), type_safe::cref(*function));
|
||||
else
|
||||
|
|
@ -161,6 +164,8 @@ namespace cppast
|
|||
class cpp_function final : public cpp_function_base
|
||||
{
|
||||
public:
|
||||
static cpp_entity_kind kind() noexcept;
|
||||
|
||||
/// Builds a [cppast::cpp_function]().
|
||||
class builder : public cpp_function_base::basic_builder<cpp_function>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace cppast
|
|||
/// See http://en.cppreference.com/w/cpp/language/storage_duration, for example.
|
||||
/// \notes These are just all the possible *keywords* used in a variable declaration,
|
||||
/// not necessarily their *semantic* meaning.
|
||||
enum cpp_storage_class_specifiers
|
||||
enum cpp_storage_class_specifiers : int
|
||||
{
|
||||
cpp_storage_class_none = 0, //< no storage class specifier given.
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,11 @@
|
|||
|
||||
using namespace cppast;
|
||||
|
||||
cpp_entity_kind cpp_function_parameter::kind() noexcept
|
||||
{
|
||||
return cpp_entity_kind::function_parameter_t;
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_function_parameter> cpp_function_parameter::build(
|
||||
const cpp_entity_index& idx, cpp_entity_id id, std::string name, std::unique_ptr<cpp_type> type,
|
||||
std::unique_ptr<cpp_expression> def)
|
||||
|
|
@ -20,10 +25,15 @@ std::unique_ptr<cpp_function_parameter> cpp_function_parameter::build(
|
|||
|
||||
cpp_entity_kind cpp_function_parameter::do_get_entity_kind() const noexcept
|
||||
{
|
||||
return cpp_entity_kind::function_parameter_t;
|
||||
return kind();
|
||||
}
|
||||
|
||||
cpp_entity_kind cpp_function::kind() noexcept
|
||||
{
|
||||
return cpp_entity_kind::function_t;
|
||||
}
|
||||
|
||||
cpp_entity_kind cpp_function::do_get_entity_kind() const noexcept
|
||||
{
|
||||
return cpp_entity_kind::function_t;
|
||||
return kind();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,11 +38,11 @@ void detail::print_cursor_info(const CXCursor& cur) noexcept
|
|||
cxstring(clang_getCursorKindSpelling(cur.kind)).c_str());
|
||||
}
|
||||
|
||||
void detail::print_tokens(const detail::cxtranslation_unit& tu, const CXFile& file,
|
||||
void detail::print_tokens(const CXTranslationUnit& tu, const CXFile& file,
|
||||
const CXCursor& cur) noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
detail::tokenizer tokenizer(tu.get(), file, cur);
|
||||
detail::tokenizer tokenizer(tu, file, cur);
|
||||
for (auto& token : tokenizer)
|
||||
std::printf("%s ", token.c_str());
|
||||
std::puts("\n");
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ namespace cppast
|
|||
|
||||
void print_cursor_info(const CXCursor& cur) noexcept;
|
||||
|
||||
void print_tokens(const cxtranslation_unit& tu, const CXFile& file,
|
||||
void print_tokens(const CXTranslationUnit& tu, const CXFile& file,
|
||||
const CXCursor& cur) noexcept;
|
||||
}
|
||||
} // namespace cppast::detail
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ using namespace cppast;
|
|||
|
||||
namespace
|
||||
{
|
||||
std::string get_expression_str(detail::token_stream& stream)
|
||||
std::string get_expression_str(detail::token_stream& stream, detail::token_iterator end)
|
||||
{
|
||||
// just concat everything
|
||||
std::string expr;
|
||||
while (!stream.done())
|
||||
while (stream.cur() != end)
|
||||
expr += stream.get().c_str();
|
||||
return expr;
|
||||
}
|
||||
|
|
@ -30,7 +30,7 @@ std::unique_ptr<cpp_expression> detail::parse_expression(const detail::parse_con
|
|||
detail::token_stream stream(tokenizer, cur);
|
||||
|
||||
auto type = parse_type(context, clang_getCursorType(cur));
|
||||
auto expr = get_expression_str(stream);
|
||||
auto expr = get_expression_str(stream, stream.end());
|
||||
if (kind == CXCursor_CallExpr && (expr.empty() || expr.back() != ')'))
|
||||
{
|
||||
// we have a call expression that doesn't end in a closing parentheses
|
||||
|
|
@ -47,12 +47,13 @@ std::unique_ptr<cpp_expression> detail::parse_expression(const detail::parse_con
|
|||
return cpp_unexposed_expression::build(std::move(type), std::move(expr));
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_expression> detail::parse_raw_expression(const parse_context& context,
|
||||
token_stream& stream,
|
||||
const CXType& type)
|
||||
std::unique_ptr<cpp_expression> detail::parse_raw_expression(const parse_context&,
|
||||
token_stream& stream,
|
||||
token_iterator end,
|
||||
std::unique_ptr<cpp_type> type)
|
||||
{
|
||||
if (stream.done())
|
||||
return nullptr;
|
||||
auto expr = get_expression_str(stream);
|
||||
return cpp_unexposed_expression::build(parse_type(context, type), std::move(expr));
|
||||
auto expr = get_expression_str(stream, end);
|
||||
return cpp_unexposed_expression::build(std::move(type), std::move(expr));
|
||||
}
|
||||
|
|
|
|||
128
src/libclang/function_parser.cpp
Normal file
128
src/libclang/function_parser.cpp
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
// 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_function.hpp>
|
||||
|
||||
#include "libclang_visitor.hpp"
|
||||
#include "parse_functions.hpp"
|
||||
|
||||
using namespace cppast;
|
||||
|
||||
namespace
|
||||
{
|
||||
std::unique_ptr<cpp_function_parameter> parse_parameter(const detail::parse_context& context,
|
||||
const CXCursor& cur)
|
||||
{
|
||||
auto name = detail::get_cursor_name(cur);
|
||||
auto type = detail::parse_type(context, clang_getCursorType(cur));
|
||||
|
||||
std::unique_ptr<cpp_expression> default_value;
|
||||
detail::visit_children(cur, [&](const CXCursor& child) {
|
||||
DEBUG_ASSERT(clang_isExpression(child.kind) && !default_value,
|
||||
detail::parse_error_handler{}, child,
|
||||
"unexpected child cursor of function parameter");
|
||||
default_value = detail::parse_expression(context, child);
|
||||
});
|
||||
|
||||
return cpp_function_parameter::build(*context.idx, detail::get_entity_id(cur), name.c_str(),
|
||||
std::move(type), std::move(default_value));
|
||||
}
|
||||
|
||||
template <class Builder>
|
||||
void add_parameters(const detail::parse_context& context, Builder& builder, const CXCursor& cur)
|
||||
{
|
||||
detail::visit_children(cur, [&](const CXCursor& child) {
|
||||
if (clang_getCursorKind(child) != CXCursor_ParmDecl)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
auto parameter = parse_parameter(context, child);
|
||||
builder.add_parameter(std::move(parameter));
|
||||
}
|
||||
catch (detail::parse_error& ex)
|
||||
{
|
||||
context.logger->log("libclang parser", ex.get_diagnostic());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void skip_parameters(detail::token_stream& stream)
|
||||
{
|
||||
detail::skip_brackets(stream);
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_expression> try_parse_noexcept(detail::token_stream& stream,
|
||||
const detail::parse_context& context)
|
||||
{
|
||||
if (!detail::skip_if(stream, "noexcept"))
|
||||
return nullptr;
|
||||
|
||||
auto type = cpp_builtin_type::build("bool");
|
||||
if (stream.peek().value() != "(")
|
||||
return cpp_literal_expression::build(std::move(type), "true");
|
||||
|
||||
auto closing = detail::find_closing_bracket(stream);
|
||||
|
||||
detail::skip(stream, "(");
|
||||
auto expr = detail::parse_raw_expression(context, stream, closing, std::move(type));
|
||||
detail::skip(stream, ")");
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
cpp_function_body_kind parse_body_kind(detail::token_stream& stream)
|
||||
{
|
||||
if (detail::skip_if(stream, "default"))
|
||||
return cpp_function_defaulted;
|
||||
else if (detail::skip_if(stream, "delete"))
|
||||
return cpp_function_deleted;
|
||||
DEBUG_UNREACHABLE(detail::parse_error_handler{}, stream.cursor(),
|
||||
"unexpected token for function body kind");
|
||||
return cpp_function_declaration;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_entity> detail::parse_cpp_function(const detail::parse_context& context,
|
||||
const CXCursor& cur)
|
||||
{
|
||||
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_FunctionDecl, detail::assert_handler{});
|
||||
auto name = detail::get_cursor_name(cur);
|
||||
|
||||
cpp_function::builder builder(name.c_str(),
|
||||
detail::parse_type(context, clang_getCursorResultType(cur)));
|
||||
add_parameters(context, builder, cur);
|
||||
if (clang_Cursor_isVariadic(cur))
|
||||
builder.is_variadic();
|
||||
builder.storage_class(detail::get_storage_class(cur));
|
||||
|
||||
detail::tokenizer tokenizer(context.tu, context.file, cur);
|
||||
detail::token_stream stream(tokenizer, cur);
|
||||
|
||||
// parse prefix
|
||||
while (!detail::skip_if(stream, name.c_str()))
|
||||
{
|
||||
if (detail::skip_if(stream, "constexpr"))
|
||||
builder.is_constexpr();
|
||||
else
|
||||
stream.bump();
|
||||
}
|
||||
// skip parameters
|
||||
skip_parameters(stream);
|
||||
|
||||
auto body = clang_isCursorDefinition(cur) ? cpp_function_definition : cpp_function_declaration;
|
||||
// parse suffix
|
||||
// tokenizer only tokenizes signature, so !stream.done() is sufficient
|
||||
while (!stream.done())
|
||||
{
|
||||
if (auto expr = try_parse_noexcept(stream, context))
|
||||
builder.noexcept_condition(std::move(expr));
|
||||
else if (skip_if(stream, "="))
|
||||
body = parse_body_kind(stream);
|
||||
else
|
||||
stream.bump();
|
||||
}
|
||||
|
||||
return builder.finish(*context.idx, detail::get_entity_id(cur), body);
|
||||
}
|
||||
|
|
@ -156,7 +156,7 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
return tu;
|
||||
return detail::cxtranslation_unit(tu);
|
||||
}
|
||||
|
||||
unsigned get_line_no(const CXCursor& cursor)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
#include "parse_functions.hpp"
|
||||
|
||||
#include <cppast/cpp_storage_class_specifiers.hpp>
|
||||
|
||||
using namespace cppast;
|
||||
|
||||
cpp_entity_id detail::get_entity_id(const CXCursor& cur)
|
||||
|
|
@ -18,6 +20,35 @@ detail::cxstring detail::get_cursor_name(const CXCursor& cur)
|
|||
return cxstring(clang_getCursorSpelling(cur));
|
||||
}
|
||||
|
||||
cpp_storage_class_specifiers detail::get_storage_class(const CXCursor& cur)
|
||||
{
|
||||
switch (clang_Cursor_getStorageClass(cur))
|
||||
{
|
||||
case CX_SC_Invalid:
|
||||
break;
|
||||
|
||||
case CX_SC_None:
|
||||
return cpp_storage_class_none;
|
||||
|
||||
case CX_SC_Auto:
|
||||
case CX_SC_Register:
|
||||
return cpp_storage_class_auto;
|
||||
|
||||
case CX_SC_Extern:
|
||||
return cpp_storage_class_extern;
|
||||
case CX_SC_Static:
|
||||
return cpp_storage_class_static;
|
||||
|
||||
case CX_SC_PrivateExtern:
|
||||
case CX_SC_OpenCLWorkGroupLocal:
|
||||
// non-exposed storage classes
|
||||
return cpp_storage_class_auto;
|
||||
}
|
||||
|
||||
DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, "unexpected storage class");
|
||||
return cpp_storage_class_auto;
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& context,
|
||||
const CXCursor& cur) try
|
||||
{
|
||||
|
|
@ -54,6 +85,9 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
|
|||
case CXCursor_FieldDecl:
|
||||
return parse_cpp_member_variable(context, cur);
|
||||
|
||||
case CXCursor_FunctionDecl:
|
||||
return parse_cpp_function(context, cur);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ namespace cppast
|
|||
{
|
||||
class cpp_expression;
|
||||
class cpp_type;
|
||||
enum cpp_storage_class_specifiers : int;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
|
@ -26,6 +27,9 @@ namespace cppast
|
|||
// as then you won't get it "as-is"
|
||||
cxstring get_cursor_name(const CXCursor& cur);
|
||||
|
||||
// note: does not handle thread_local
|
||||
cpp_storage_class_specifiers get_storage_class(const CXCursor& cur);
|
||||
|
||||
struct parse_context
|
||||
{
|
||||
CXTranslationUnit tu;
|
||||
|
|
@ -39,12 +43,13 @@ namespace cppast
|
|||
std::unique_ptr<cpp_expression> parse_expression(const parse_context& context,
|
||||
const CXCursor& cur);
|
||||
// parse the expression starting at the current token in the stream
|
||||
// and ends at the end of the stream
|
||||
// and ends at the given iterator
|
||||
// this is required for situations where there is no expression cursor exposed,
|
||||
// like member initializers
|
||||
std::unique_ptr<cpp_expression> parse_raw_expression(const parse_context& context,
|
||||
token_stream& stream,
|
||||
const CXType& type);
|
||||
std::unique_ptr<cpp_expression> parse_raw_expression(const parse_context& context,
|
||||
token_stream& stream,
|
||||
token_iterator end,
|
||||
std::unique_ptr<cpp_type> type);
|
||||
|
||||
// parse_entity() dispatches on the cursor type
|
||||
// it calls one of the other parse functions defined elsewhere
|
||||
|
|
@ -76,6 +81,9 @@ namespace cppast
|
|||
std::unique_ptr<cpp_entity> parse_cpp_member_variable(const parse_context& context,
|
||||
const CXCursor& cur);
|
||||
|
||||
std::unique_ptr<cpp_entity> parse_cpp_function(const parse_context& context,
|
||||
const CXCursor& cur);
|
||||
|
||||
std::unique_ptr<cpp_entity> parse_entity(const parse_context& context, const CXCursor& cur);
|
||||
}
|
||||
} // namespace cppast::detail
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ namespace cppast
|
|||
{
|
||||
}
|
||||
|
||||
raii_wrapper(T obj) noexcept : obj_(obj)
|
||||
explicit raii_wrapper(T obj) noexcept : obj_(obj)
|
||||
{
|
||||
DEBUG_ASSERT(obj_, detail::assert_handler{});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,11 +124,21 @@ namespace cppast
|
|||
return cursor_;
|
||||
}
|
||||
|
||||
token_iterator begin() const noexcept
|
||||
{
|
||||
return begin_;
|
||||
}
|
||||
|
||||
token_iterator cur() const noexcept
|
||||
{
|
||||
return cur_;
|
||||
}
|
||||
|
||||
token_iterator end() const noexcept
|
||||
{
|
||||
return end_;
|
||||
}
|
||||
|
||||
void set_cur(token_iterator iter) noexcept
|
||||
{
|
||||
cur_ = iter;
|
||||
|
|
|
|||
|
|
@ -183,6 +183,9 @@ namespace
|
|||
return cpp_ref_none;
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_type> parse_type_impl(const detail::parse_context& context,
|
||||
const CXType& type);
|
||||
|
||||
std::unique_ptr<cpp_expression> parse_array_size(const CXType& type)
|
||||
{
|
||||
auto size = clang_getArraySize(type);
|
||||
|
|
@ -214,8 +217,27 @@ namespace
|
|||
size_expr.rend()));
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_type> parse_type_impl(const detail::parse_context& context,
|
||||
const CXType& type);
|
||||
std::unique_ptr<cpp_type> try_parse_array_type(const detail::parse_context& context,
|
||||
const CXType& type)
|
||||
{
|
||||
auto canonical = clang_getCanonicalType(type);
|
||||
auto value_type = clang_getArrayElementType(type);
|
||||
if (value_type.kind == CXType_Invalid)
|
||||
{
|
||||
// value_type is invalid, however type can still be an array
|
||||
// as there seems to be a libclang bug
|
||||
// only if the canonical type is not an array,
|
||||
// is it truly not an array
|
||||
value_type = clang_getArrayElementType(canonical);
|
||||
if (value_type.kind == CXType_Invalid)
|
||||
return nullptr;
|
||||
// we have an array, even though type isn't one directly
|
||||
// only downside of this workaround: we've stripped away typedefs
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
template <class Builder>
|
||||
std::unique_ptr<cpp_type> add_parameters(Builder& builder, const detail::parse_context& context,
|
||||
|
|
@ -317,6 +339,9 @@ namespace
|
|||
// 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))
|
||||
// same deal here
|
||||
return atype;
|
||||
return cpp_unexposed_type::build(get_type_spelling(type).c_str());
|
||||
|
||||
case CXType_Void:
|
||||
|
|
@ -379,11 +404,7 @@ namespace
|
|||
case CXType_VariableArray:
|
||||
case CXType_DependentSizedArray:
|
||||
case CXType_ConstantArray:
|
||||
{
|
||||
auto size = parse_array_size(type);
|
||||
auto value_type = parse_type_impl(context, clang_getArrayElementType(type));
|
||||
return cpp_array_type::build(std::move(value_type), std::move(size));
|
||||
}
|
||||
return try_parse_array_type(context, type);
|
||||
|
||||
case CXType_FunctionNoProto:
|
||||
case CXType_FunctionProto:
|
||||
|
|
|
|||
|
|
@ -28,35 +28,6 @@ namespace
|
|||
});
|
||||
return expression;
|
||||
}
|
||||
|
||||
cpp_storage_class_specifiers parse_storage_class(const CXCursor& cur)
|
||||
{
|
||||
switch (clang_Cursor_getStorageClass(cur))
|
||||
{
|
||||
case CX_SC_Invalid:
|
||||
break;
|
||||
|
||||
case CX_SC_None:
|
||||
return cpp_storage_class_none;
|
||||
|
||||
case CX_SC_Auto:
|
||||
case CX_SC_Register:
|
||||
return cpp_storage_class_auto;
|
||||
|
||||
case CX_SC_Extern:
|
||||
return cpp_storage_class_extern;
|
||||
case CX_SC_Static:
|
||||
return cpp_storage_class_static;
|
||||
|
||||
case CX_SC_PrivateExtern:
|
||||
case CX_SC_OpenCLWorkGroupLocal:
|
||||
// non-exposed storage classes
|
||||
return cpp_storage_class_auto;
|
||||
}
|
||||
|
||||
DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, "unexpected storage class");
|
||||
return cpp_storage_class_auto;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_entity> detail::parse_cpp_variable(const detail::parse_context& context,
|
||||
|
|
@ -66,7 +37,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_variable(const detail::parse_conte
|
|||
|
||||
auto name = get_cursor_name(cur);
|
||||
auto type = parse_type(context, clang_getCursorType(cur));
|
||||
auto storage_class = parse_storage_class(cur);
|
||||
auto storage_class = get_storage_class(cur);
|
||||
auto is_constexpr = false;
|
||||
|
||||
// just look for thread local or constexpr
|
||||
|
|
@ -118,7 +89,8 @@ 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, clang_getCursorType(cur));
|
||||
auto default_value = parse_raw_expression(context, stream, stream.end(),
|
||||
parse_type(context, clang_getCursorType(cur)));
|
||||
|
||||
return cpp_member_variable::build(*context.idx, get_entity_id(cur), name.c_str(),
|
||||
std::move(type), std::move(default_value), is_mutable);
|
||||
|
|
|
|||
195
test/cpp_function.cpp
Normal file
195
test/cpp_function.cpp
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
// 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_function.hpp>
|
||||
#include <cppast/cpp_array_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();
|
||||
int b(int a, float* b = nullptr);
|
||||
int (&c(int a, ...))[10];
|
||||
|
||||
// noexcept conditions
|
||||
void d() noexcept;
|
||||
void e() noexcept(false);
|
||||
void f() noexcept(noexcept(d()));
|
||||
|
||||
// storage class + constexpr
|
||||
extern void g();
|
||||
static void h();
|
||||
constexpr void i();
|
||||
static constexpr void j();
|
||||
|
||||
// body
|
||||
void k() = delete;
|
||||
void l()
|
||||
{
|
||||
// might confuse parser
|
||||
auto b = noexcept(g());
|
||||
}
|
||||
)";
|
||||
|
||||
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) {
|
||||
REQUIRE(!func.is_friend());
|
||||
|
||||
if (func.name() == "a" || func.name() == "b" || func.name() == "c")
|
||||
{
|
||||
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("void")));
|
||||
REQUIRE(count_children(func) == 0u);
|
||||
REQUIRE(!func.is_variadic());
|
||||
}
|
||||
else if (func.name() == "b")
|
||||
{
|
||||
REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build("int")));
|
||||
|
||||
auto count = 0u;
|
||||
for (auto& param : func)
|
||||
{
|
||||
if (param.name() == "a")
|
||||
{
|
||||
REQUIRE(equal_types(idx, param.type(), *cpp_builtin_type::build("int")));
|
||||
REQUIRE(!param.default_value());
|
||||
}
|
||||
else if (param.name() == "b")
|
||||
{
|
||||
REQUIRE(
|
||||
equal_types(idx, param.type(), *cpp_pointer_type::build(
|
||||
cpp_builtin_type::build("float"))));
|
||||
REQUIRE(param.default_value());
|
||||
REQUIRE(equal_expressions(param.default_value().value(),
|
||||
*cpp_unexposed_expression::
|
||||
build(cpp_pointer_type::build(
|
||||
cpp_builtin_type::build("float")),
|
||||
"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("int"),
|
||||
cpp_literal_expression::
|
||||
build(cpp_builtin_type::build(
|
||||
"unsigned long long"),
|
||||
"10")),
|
||||
cpp_ref_lvalue)));
|
||||
|
||||
auto count = 0u;
|
||||
for (auto& param : func)
|
||||
{
|
||||
if (param.name() == "a")
|
||||
{
|
||||
REQUIRE(equal_types(idx, param.type(), *cpp_builtin_type::build("int")));
|
||||
REQUIRE(!param.default_value());
|
||||
}
|
||||
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("void")));
|
||||
REQUIRE(count_children(func) == 0u);
|
||||
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("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),
|
||||
"false")));
|
||||
else if (func.name() == "f")
|
||||
REQUIRE(equal_expressions(func.noexcept_condition().value(),
|
||||
*cpp_unexposed_expression::build(std::move(bool_t),
|
||||
"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("void")));
|
||||
REQUIRE(count_children(func) == 0u);
|
||||
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("void")));
|
||||
REQUIRE(count_children(func) == 0u);
|
||||
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")
|
||||
check_body(func, cpp_function_definition);
|
||||
}
|
||||
else
|
||||
REQUIRE(false);
|
||||
});
|
||||
REQUIRE(count == 12u);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue