Parse cpp_member_function
This commit is contained in:
parent
39b5b01ae3
commit
85ca226117
6 changed files with 487 additions and 45 deletions
|
|
@ -5,30 +5,72 @@
|
||||||
#ifndef CPPAST_CPP_MEMBER_FUNCTION_HPP_INCLUDED
|
#ifndef CPPAST_CPP_MEMBER_FUNCTION_HPP_INCLUDED
|
||||||
#define CPPAST_CPP_MEMBER_FUNCTION_HPP_INCLUDED
|
#define CPPAST_CPP_MEMBER_FUNCTION_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <type_safe/flag_set.hpp>
|
||||||
|
|
||||||
#include <cppast/cpp_function.hpp>
|
#include <cppast/cpp_function.hpp>
|
||||||
|
|
||||||
namespace cppast
|
namespace cppast
|
||||||
{
|
{
|
||||||
/// The `virtual`-ness of a member function.
|
/// The `virtual`-ness of a member function.
|
||||||
enum cpp_virtual
|
///
|
||||||
|
/// This is a [ts::flag_set]() `enum`.
|
||||||
|
/// \notes It does not specify whether a member function is `virtual` or not,
|
||||||
|
/// only the kind of `virtual`.
|
||||||
|
/// \notes As surprising as it may be, any of these can be used in combination,
|
||||||
|
/// i.e. you can have a `final` non-overriding function or an overriding pure `virtual` function.
|
||||||
|
enum class cpp_virtual_flags
|
||||||
{
|
{
|
||||||
cpp_virtual_none, //< Not `virtual`.
|
pure, //< Set if the function is pure.
|
||||||
cpp_virtual_pure, //< Pure `virtual` function.
|
override, //< Set if the function overrides a base class function.
|
||||||
cpp_virtual_new, //< New `virtual` function.
|
final, //< Set if the function is marked `final`.
|
||||||
cpp_virtual_override, //< Overriden `virtual` function (attribute doesn't matter).
|
|
||||||
cpp_virtual_final, //< `final` `virtual` function.
|
|
||||||
};
|
};
|
||||||
|
} // namespace cppast
|
||||||
|
|
||||||
/// \returns Whether or not the given flag means the function is `virtual`.
|
/// \exclude
|
||||||
inline bool is_virtual(cpp_virtual virt) noexcept
|
namespace type_safe
|
||||||
{
|
{
|
||||||
return virt != cpp_virtual_none;
|
template <>
|
||||||
|
struct flag_set_traits<cppast::cpp_virtual_flags> : std::true_type
|
||||||
|
{
|
||||||
|
static constexpr std::size_t size() noexcept
|
||||||
|
{
|
||||||
|
return 3u;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace type_safe
|
||||||
|
|
||||||
|
namespace cppast
|
||||||
|
{
|
||||||
|
/// The `virtual` information of a member function.
|
||||||
|
///
|
||||||
|
/// This is an optional of the combination of the [cppast::cpp_virtual_flags]().
|
||||||
|
/// If the optional has a value, the member function is `virtual`,
|
||||||
|
/// and the [ts::flag_set]() describes additional information.
|
||||||
|
using cpp_virtual = type_safe::optional<type_safe::flag_set<cpp_virtual_flags>>;
|
||||||
|
|
||||||
|
/// \returns Whether or not a member function is `virtual`.
|
||||||
|
inline bool is_virtual(const cpp_virtual& virt) noexcept
|
||||||
|
{
|
||||||
|
return virt.has_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \returns Whether or not the given flag means the function overrides a `virtual` function.
|
/// \returns Whether or not a member function is pure.
|
||||||
inline bool is_overriden(cpp_virtual virt) noexcept
|
inline bool is_pure(const cpp_virtual& virt) noexcept
|
||||||
{
|
{
|
||||||
return virt == cpp_virtual_override || virt == cpp_virtual_final;
|
return static_cast<bool>(virt.value_or(cpp_virtual_flags::final) & cpp_virtual_flags::pure);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \returns Whether or not a member function overrides another one.
|
||||||
|
inline bool is_overriding(const cpp_virtual& virt) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<bool>(virt.value_or(cpp_virtual_flags::pure)
|
||||||
|
& cpp_virtual_flags::override);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \returns Whether or not a member function is `final`.
|
||||||
|
inline bool is_final(const cpp_virtual& virt) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<bool>(virt.value_or(cpp_virtual_flags::pure) & cpp_virtual_flags::final);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Base classes for all regular member function.
|
/// Base classes for all regular member function.
|
||||||
|
|
@ -43,8 +85,14 @@ namespace cppast
|
||||||
return *return_type_;
|
return *return_type_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \returns Whether or not it is `virtual`.
|
||||||
|
bool is_virtual() const noexcept
|
||||||
|
{
|
||||||
|
return virtual_info().has_value();
|
||||||
|
}
|
||||||
|
|
||||||
/// \returns The `virtual`-ness of the member function.
|
/// \returns The `virtual`-ness of the member function.
|
||||||
cpp_virtual virtual_info() const noexcept
|
const cpp_virtual& virtual_info() const noexcept
|
||||||
{
|
{
|
||||||
return virtual_;
|
return virtual_;
|
||||||
}
|
}
|
||||||
|
|
@ -88,7 +136,7 @@ namespace cppast
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \effects Sets the `virtual`-ness of the function.
|
/// \effects Sets the `virtual`-ness of the function.
|
||||||
void virtual_info(cpp_virtual virt) noexcept
|
void virtual_info(type_safe::flag_set<cpp_virtual_flags> virt) noexcept
|
||||||
{
|
{
|
||||||
static_cast<cpp_member_function_base&>(*this->function).virtual_ = virt;
|
static_cast<cpp_member_function_base&>(*this->function).virtual_ = virt;
|
||||||
}
|
}
|
||||||
|
|
@ -104,7 +152,6 @@ namespace cppast
|
||||||
cpp_member_function_base(std::string name, std::unique_ptr<cpp_type> return_type)
|
cpp_member_function_base(std::string name, std::unique_ptr<cpp_type> return_type)
|
||||||
: cpp_function_base(std::move(name)),
|
: cpp_function_base(std::move(name)),
|
||||||
return_type_(std::move(return_type)),
|
return_type_(std::move(return_type)),
|
||||||
virtual_(cpp_virtual_none),
|
|
||||||
cv_(cpp_cv_none),
|
cv_(cpp_cv_none),
|
||||||
ref_(cpp_ref_none),
|
ref_(cpp_ref_none),
|
||||||
constexpr_(false)
|
constexpr_(false)
|
||||||
|
|
@ -123,6 +170,8 @@ namespace cppast
|
||||||
class cpp_member_function final : public cpp_member_function_base
|
class cpp_member_function final : public cpp_member_function_base
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static cpp_entity_kind kind() noexcept;
|
||||||
|
|
||||||
/// Builder for [cppast::cpp_member_function]().
|
/// Builder for [cppast::cpp_member_function]().
|
||||||
class builder : public basic_member_builder<cpp_member_function>
|
class builder : public basic_member_builder<cpp_member_function>
|
||||||
{
|
{
|
||||||
|
|
@ -134,6 +183,8 @@ namespace cppast
|
||||||
using cpp_member_function_base::cpp_member_function_base;
|
using cpp_member_function_base::cpp_member_function_base;
|
||||||
|
|
||||||
cpp_entity_kind do_get_entity_kind() const noexcept override;
|
cpp_entity_kind do_get_entity_kind() const noexcept override;
|
||||||
|
|
||||||
|
friend basic_member_builder<cpp_member_function>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A [cppast::cpp_entity]() modelling a C++ conversion operator.
|
/// A [cppast::cpp_entity]() modelling a C++ conversion operator.
|
||||||
|
|
@ -238,6 +289,12 @@ namespace cppast
|
||||||
using basic_builder::is_variadic;
|
using basic_builder::is_variadic;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// \returns Whether or not it is `virtual`.
|
||||||
|
bool is_virtual() const noexcept
|
||||||
|
{
|
||||||
|
return virtual_info().has_value();
|
||||||
|
}
|
||||||
|
|
||||||
/// \returns The `virtual`-ness of the constructor.
|
/// \returns The `virtual`-ness of the constructor.
|
||||||
cpp_virtual virtual_info() const noexcept
|
cpp_virtual virtual_info() const noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -245,8 +302,7 @@ namespace cppast
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
cpp_destructor(std::string name)
|
cpp_destructor(std::string name) : cpp_function_base(std::move(name))
|
||||||
: cpp_function_base(std::move(name)), virtual_(cpp_virtual_none)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,16 @@
|
||||||
|
|
||||||
using namespace cppast;
|
using namespace cppast;
|
||||||
|
|
||||||
cpp_entity_kind cpp_member_function::do_get_entity_kind() const noexcept
|
cpp_entity_kind cpp_member_function::kind() noexcept
|
||||||
{
|
{
|
||||||
return cpp_entity_kind::member_function_t;
|
return cpp_entity_kind::member_function_t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cpp_entity_kind cpp_member_function::do_get_entity_kind() const noexcept
|
||||||
|
{
|
||||||
|
return kind();
|
||||||
|
}
|
||||||
|
|
||||||
cpp_entity_kind cpp_conversion_op::do_get_entity_kind() const noexcept
|
cpp_entity_kind cpp_conversion_op::do_get_entity_kind() const noexcept
|
||||||
{
|
{
|
||||||
return cpp_entity_kind::conversion_op_t;
|
return cpp_entity_kind::conversion_op_t;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
// found in the top-level directory of this distribution.
|
// found in the top-level directory of this distribution.
|
||||||
|
|
||||||
#include <cppast/cpp_function.hpp>
|
#include <cppast/cpp_function.hpp>
|
||||||
|
#include <cppast/cpp_member_function.hpp>
|
||||||
|
|
||||||
#include "libclang_visitor.hpp"
|
#include "libclang_visitor.hpp"
|
||||||
#include "parse_functions.hpp"
|
#include "parse_functions.hpp"
|
||||||
|
|
@ -53,7 +54,78 @@ namespace
|
||||||
detail::skip_brackets(stream);
|
detail::skip_brackets(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<cpp_expression> try_parse_noexcept(detail::token_stream& stream,
|
// just the tokens occurring in the prefix
|
||||||
|
struct prefix_info
|
||||||
|
{
|
||||||
|
bool is_constexpr = false;
|
||||||
|
bool is_virtual = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
prefix_info parse_prefix_info(detail::token_stream& stream, const detail::cxstring& name)
|
||||||
|
{
|
||||||
|
prefix_info result;
|
||||||
|
|
||||||
|
// just check for keywords until we've reached the function name
|
||||||
|
while (!detail::skip_if(stream, name.c_str()))
|
||||||
|
{
|
||||||
|
if (detail::skip_if(stream, "constexpr"))
|
||||||
|
result.is_constexpr = true;
|
||||||
|
else if (detail::skip_if(stream, "virtual"))
|
||||||
|
result.is_virtual = true;
|
||||||
|
else
|
||||||
|
stream.bump();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// just the tokens occurring in the suffix
|
||||||
|
struct suffix_info
|
||||||
|
{
|
||||||
|
std::unique_ptr<cpp_expression> noexcept_condition;
|
||||||
|
cpp_function_body_kind body_kind;
|
||||||
|
cpp_cv cv_qualifier = cpp_cv_none;
|
||||||
|
cpp_reference ref_qualifier = cpp_ref_none;
|
||||||
|
cpp_virtual virtual_keywords;
|
||||||
|
|
||||||
|
suffix_info(const CXCursor& cur)
|
||||||
|
: body_kind(clang_isCursorDefinition(cur) ? cpp_function_definition :
|
||||||
|
cpp_function_declaration)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
cpp_cv parse_cv(detail::token_stream& stream)
|
||||||
|
{
|
||||||
|
if (detail::skip_if(stream, "const"))
|
||||||
|
{
|
||||||
|
if (detail::skip_if(stream, "volatile"))
|
||||||
|
return cpp_cv_const_volatile;
|
||||||
|
else
|
||||||
|
return cpp_cv_const;
|
||||||
|
}
|
||||||
|
else if (detail::skip_if(stream, "volatile"))
|
||||||
|
{
|
||||||
|
if (detail::skip_if(stream, "const"))
|
||||||
|
return cpp_cv_const_volatile;
|
||||||
|
else
|
||||||
|
return cpp_cv_volatile;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return cpp_cv_none;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpp_reference parse_ref(detail::token_stream& stream)
|
||||||
|
{
|
||||||
|
if (detail::skip_if(stream, "&"))
|
||||||
|
return cpp_ref_lvalue;
|
||||||
|
else if (detail::skip_if(stream, "&&"))
|
||||||
|
return cpp_ref_rvalue;
|
||||||
|
else
|
||||||
|
return cpp_ref_none;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<cpp_expression> parse_noexcept(detail::token_stream& stream,
|
||||||
const detail::parse_context& context)
|
const detail::parse_context& context)
|
||||||
{
|
{
|
||||||
if (!detail::skip_if(stream, "noexcept"))
|
if (!detail::skip_if(stream, "noexcept"))
|
||||||
|
|
@ -72,17 +144,115 @@ namespace
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
cpp_function_body_kind parse_body_kind(detail::token_stream& stream)
|
cpp_function_body_kind parse_body_kind(detail::token_stream& stream, bool& pure_virtual)
|
||||||
{
|
{
|
||||||
|
pure_virtual = false;
|
||||||
if (detail::skip_if(stream, "default"))
|
if (detail::skip_if(stream, "default"))
|
||||||
return cpp_function_defaulted;
|
return cpp_function_defaulted;
|
||||||
else if (detail::skip_if(stream, "delete"))
|
else if (detail::skip_if(stream, "delete"))
|
||||||
return cpp_function_deleted;
|
return cpp_function_deleted;
|
||||||
|
else if (detail::skip_if(stream, "0"))
|
||||||
|
{
|
||||||
|
pure_virtual = true;
|
||||||
|
return cpp_function_declaration;
|
||||||
|
}
|
||||||
|
|
||||||
DEBUG_UNREACHABLE(detail::parse_error_handler{}, stream.cursor(),
|
DEBUG_UNREACHABLE(detail::parse_error_handler{}, stream.cursor(),
|
||||||
"unexpected token for function body kind");
|
"unexpected token for function body kind");
|
||||||
return cpp_function_declaration;
|
return cpp_function_declaration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void parse_body(detail::token_stream& stream, suffix_info& result)
|
||||||
|
{
|
||||||
|
auto pure_virtual = false;
|
||||||
|
result.body_kind = parse_body_kind(stream, pure_virtual);
|
||||||
|
if (pure_virtual)
|
||||||
|
{
|
||||||
|
if (result.virtual_keywords)
|
||||||
|
result.virtual_keywords.value() &= cpp_virtual_flags::pure;
|
||||||
|
else
|
||||||
|
result.virtual_keywords = cpp_virtual_flags::pure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// precondition: we've skipped the function parameters
|
||||||
|
suffix_info parse_suffix_info(detail::token_stream& stream,
|
||||||
|
const detail::parse_context& context)
|
||||||
|
{
|
||||||
|
suffix_info result(stream.cursor());
|
||||||
|
|
||||||
|
// syntax: <attribute> <cv> <ref> <exception>
|
||||||
|
detail::skip_attribute(stream);
|
||||||
|
result.cv_qualifier = parse_cv(stream);
|
||||||
|
result.ref_qualifier = parse_ref(stream);
|
||||||
|
if (detail::skip_if(stream, "throw"))
|
||||||
|
// just because I can
|
||||||
|
detail::skip_brackets(stream);
|
||||||
|
result.noexcept_condition = parse_noexcept(stream, context);
|
||||||
|
|
||||||
|
// check if we have leftovers of the return type
|
||||||
|
// i.e.: `void (*foo(int a, int b) const)(int)`;
|
||||||
|
// ^^^^^^- attributes
|
||||||
|
// ^^^^^^- leftovers
|
||||||
|
// if we have a closing parenthesis, skip brackets
|
||||||
|
if (detail::skip_if(stream, ")"))
|
||||||
|
detail::skip_brackets(stream);
|
||||||
|
|
||||||
|
// check for trailing return type
|
||||||
|
if (detail::skip_if(stream, "->"))
|
||||||
|
{
|
||||||
|
// this is rather tricky to skip
|
||||||
|
// so loop over all tokens and see if matching keytokens occur
|
||||||
|
// note that this isn't quite correct
|
||||||
|
// use a heuristic to skip brackets, which should be good enough
|
||||||
|
while (!stream.done())
|
||||||
|
{
|
||||||
|
if (stream.peek() == "(" || stream.peek() == "[" || stream.peek() == "<"
|
||||||
|
|| stream.peek() == "{")
|
||||||
|
detail::skip_brackets(stream);
|
||||||
|
else if (detail::skip_if(stream, "override"))
|
||||||
|
{
|
||||||
|
if (result.virtual_keywords)
|
||||||
|
result.virtual_keywords.value() |= cpp_virtual_flags::override;
|
||||||
|
else
|
||||||
|
result.virtual_keywords = cpp_virtual_flags::override;
|
||||||
|
}
|
||||||
|
else if (detail::skip_if(stream, "final"))
|
||||||
|
{
|
||||||
|
if (result.virtual_keywords)
|
||||||
|
result.virtual_keywords.value() |= cpp_virtual_flags::final;
|
||||||
|
else
|
||||||
|
result.virtual_keywords = cpp_virtual_flags::final;
|
||||||
|
}
|
||||||
|
else if (detail::skip_if(stream, "="))
|
||||||
|
parse_body(stream, result);
|
||||||
|
else
|
||||||
|
stream.bump();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// syntax: <virtuals> <body>
|
||||||
|
if (detail::skip_if(stream, "override"))
|
||||||
|
{
|
||||||
|
result.virtual_keywords = cpp_virtual_flags::override;
|
||||||
|
if (detail::skip_if(stream, "final"))
|
||||||
|
result.virtual_keywords.value() |= cpp_virtual_flags::final;
|
||||||
|
}
|
||||||
|
else if (detail::skip_if(stream, "final"))
|
||||||
|
{
|
||||||
|
result.virtual_keywords = cpp_virtual_flags::final;
|
||||||
|
if (detail::skip_if(stream, "override"))
|
||||||
|
result.virtual_keywords.value() |= cpp_virtual_flags::override;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (detail::skip_if(stream, "="))
|
||||||
|
parse_body(stream, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<cpp_entity> parse_cpp_function_impl(const detail::parse_context& context,
|
std::unique_ptr<cpp_entity> parse_cpp_function_impl(const detail::parse_context& context,
|
||||||
const CXCursor& cur)
|
const CXCursor& cur)
|
||||||
{
|
{
|
||||||
|
|
@ -98,32 +268,22 @@ namespace
|
||||||
detail::tokenizer tokenizer(context.tu, context.file, cur);
|
detail::tokenizer tokenizer(context.tu, context.file, cur);
|
||||||
detail::token_stream stream(tokenizer, cur);
|
detail::token_stream stream(tokenizer, cur);
|
||||||
|
|
||||||
// parse prefix
|
auto prefix = parse_prefix_info(stream, name);
|
||||||
while (!detail::skip_if(stream, name.c_str()))
|
DEBUG_ASSERT(!prefix.is_virtual, detail::parse_error_handler{}, cur,
|
||||||
{
|
"unexpected tokens in function prefix");
|
||||||
if (detail::skip_if(stream, "constexpr"))
|
if (prefix.is_constexpr)
|
||||||
builder.is_constexpr();
|
builder.is_constexpr();
|
||||||
else
|
|
||||||
stream.bump();
|
|
||||||
}
|
|
||||||
// skip parameters
|
|
||||||
skip_parameters(stream);
|
skip_parameters(stream);
|
||||||
|
|
||||||
auto body =
|
auto suffix = parse_suffix_info(stream, context);
|
||||||
clang_isCursorDefinition(cur) ? cpp_function_definition : cpp_function_declaration;
|
DEBUG_ASSERT(suffix.cv_qualifier == cpp_cv_none && suffix.ref_qualifier == cpp_ref_none
|
||||||
// parse suffix
|
&& !suffix.virtual_keywords,
|
||||||
// tokenizer only tokenizes signature, so !stream.done() is sufficient
|
detail::parse_error_handler{}, cur, "unexpected tokens in function suffix");
|
||||||
while (!stream.done())
|
if (suffix.noexcept_condition)
|
||||||
{
|
builder.noexcept_condition(std::move(suffix.noexcept_condition));
|
||||||
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);
|
return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -142,3 +302,90 @@ std::unique_ptr<cpp_entity> detail::try_parse_static_cpp_function(
|
||||||
return parse_cpp_function_impl(context, cur);
|
return parse_cpp_function_impl(context, cur);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool overrides_function(const CXCursor& cur)
|
||||||
|
{
|
||||||
|
CXCursor* overrides = nullptr;
|
||||||
|
auto num = 0u;
|
||||||
|
clang_getOverriddenCursors(cur, &overrides, &num);
|
||||||
|
clang_disposeOverriddenCursors(overrides);
|
||||||
|
return num != 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpp_virtual calculate_virtual(const CXCursor& cur, bool virtual_keyword,
|
||||||
|
const cpp_virtual& virtual_suffix)
|
||||||
|
{
|
||||||
|
if (!clang_CXXMethod_isVirtual(cur))
|
||||||
|
{
|
||||||
|
// not a virtual function, ensure it wasn't parsed that way
|
||||||
|
DEBUG_ASSERT(!virtual_keyword && !virtual_suffix.has_value(),
|
||||||
|
detail::parse_error_handler{}, cur, "virtualness not parsed properly");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
else if (clang_CXXMethod_isPureVirtual(cur))
|
||||||
|
{
|
||||||
|
// pure virtual function - all information in the suffix
|
||||||
|
DEBUG_ASSERT(virtual_suffix.has_value()
|
||||||
|
&& virtual_suffix.value() & cpp_virtual_flags::pure,
|
||||||
|
detail::parse_error_handler{}, cur, "pure virtual not detected");
|
||||||
|
return virtual_suffix;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// non-pure virtual function
|
||||||
|
DEBUG_ASSERT(!virtual_suffix.has_value()
|
||||||
|
|| !(virtual_suffix.value() & cpp_virtual_flags::pure),
|
||||||
|
detail::parse_error_handler{}, cur,
|
||||||
|
"pure virtual function detected, even though it isn't");
|
||||||
|
// calculate whether it overrides
|
||||||
|
auto overrides = !virtual_keyword
|
||||||
|
|| (virtual_suffix.has_value()
|
||||||
|
&& virtual_suffix.value() & cpp_virtual_flags::override)
|
||||||
|
|| overrides_function(cur);
|
||||||
|
|
||||||
|
// result are all the flags in the suffix
|
||||||
|
auto result = virtual_suffix;
|
||||||
|
if (!result)
|
||||||
|
// make sure it isn't empty
|
||||||
|
result.emplace();
|
||||||
|
if (overrides)
|
||||||
|
// make sure it contains the override flag
|
||||||
|
result.value() |= cpp_virtual_flags::override;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<cpp_entity> detail::parse_cpp_member_function(const detail::parse_context& context,
|
||||||
|
const CXCursor& cur)
|
||||||
|
{
|
||||||
|
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_CXXMethod, detail::assert_handler{});
|
||||||
|
auto name = detail::get_cursor_name(cur);
|
||||||
|
|
||||||
|
cpp_member_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();
|
||||||
|
|
||||||
|
detail::tokenizer tokenizer(context.tu, context.file, cur);
|
||||||
|
detail::token_stream stream(tokenizer, cur);
|
||||||
|
|
||||||
|
auto prefix = parse_prefix_info(stream, name);
|
||||||
|
if (prefix.is_constexpr)
|
||||||
|
builder.is_constexpr();
|
||||||
|
|
||||||
|
skip_parameters(stream);
|
||||||
|
|
||||||
|
auto suffix = parse_suffix_info(stream, context);
|
||||||
|
builder.cv_ref_qualifier(suffix.cv_qualifier, suffix.ref_qualifier);
|
||||||
|
if (suffix.noexcept_condition)
|
||||||
|
builder.noexcept_condition(std::move(suffix.noexcept_condition));
|
||||||
|
if (auto virt = calculate_virtual(cur, prefix.is_virtual, suffix.virtual_keywords))
|
||||||
|
builder.virtual_info(virt.value());
|
||||||
|
|
||||||
|
return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
|
||||||
// check for static function
|
// check for static function
|
||||||
if (auto func = try_parse_static_cpp_function(context, cur))
|
if (auto func = try_parse_static_cpp_function(context, cur))
|
||||||
return func;
|
return func;
|
||||||
break;
|
return parse_cpp_member_function(context, cur);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,8 @@ namespace cppast
|
||||||
|
|
||||||
std::unique_ptr<cpp_entity> parse_cpp_function(const parse_context& context,
|
std::unique_ptr<cpp_entity> parse_cpp_function(const parse_context& context,
|
||||||
const CXCursor& cur);
|
const CXCursor& cur);
|
||||||
|
std::unique_ptr<cpp_entity> parse_cpp_member_function(const parse_context& context,
|
||||||
|
const CXCursor& cur);
|
||||||
|
|
||||||
std::unique_ptr<cpp_entity> parse_entity(const parse_context& context, const CXCursor& cur);
|
std::unique_ptr<cpp_entity> parse_entity(const parse_context& context, const CXCursor& cur);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
132
test/cpp_member_function.cpp
Normal file
132
test/cpp_member_function.cpp
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
// 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_member_function.hpp>
|
||||||
|
|
||||||
|
#include "test_parser.hpp"
|
||||||
|
|
||||||
|
using namespace cppast;
|
||||||
|
|
||||||
|
TEST_CASE("cpp_member_function")
|
||||||
|
{
|
||||||
|
auto code = R"(
|
||||||
|
// no need to test parameters/return types
|
||||||
|
struct foo
|
||||||
|
{
|
||||||
|
void a();
|
||||||
|
void b() noexcept;
|
||||||
|
|
||||||
|
void c() const;
|
||||||
|
auto d() const volatile -> void;
|
||||||
|
void e() &;
|
||||||
|
void f() const volatile &&;
|
||||||
|
|
||||||
|
virtual void g();
|
||||||
|
virtual void h() = 0;
|
||||||
|
|
||||||
|
void i() {}
|
||||||
|
void j() = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bar : foo
|
||||||
|
{
|
||||||
|
void g();
|
||||||
|
virtual auto h() -> void override final;
|
||||||
|
};
|
||||||
|
)";
|
||||||
|
|
||||||
|
cpp_entity_index idx;
|
||||||
|
auto file = parse(idx, "cpp_member_function.cpp", code);
|
||||||
|
auto count = test_visit<cpp_member_function>(*file, [&](const cpp_member_function& func) {
|
||||||
|
REQUIRE(count_children(func) == 0u);
|
||||||
|
REQUIRE(!func.is_variadic());
|
||||||
|
REQUIRE(!func.is_constexpr());
|
||||||
|
REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build("void")));
|
||||||
|
if (func.name() != "b")
|
||||||
|
REQUIRE(!func.noexcept_condition());
|
||||||
|
if (func.name() != "g" && func.name() != "h")
|
||||||
|
REQUIRE(!func.virtual_info());
|
||||||
|
if (func.name() != "i" && func.name() != "j")
|
||||||
|
REQUIRE(func.body_kind() == cpp_function_declaration);
|
||||||
|
|
||||||
|
if (func.name() == "a")
|
||||||
|
{
|
||||||
|
REQUIRE(func.cv_qualifier() == cpp_cv_none);
|
||||||
|
REQUIRE(func.ref_qualifier() == cpp_ref_none);
|
||||||
|
}
|
||||||
|
else if (func.name() == "b")
|
||||||
|
{
|
||||||
|
REQUIRE(func.noexcept_condition());
|
||||||
|
REQUIRE(
|
||||||
|
equal_expressions(func.noexcept_condition().value(),
|
||||||
|
*cpp_literal_expression::build(cpp_builtin_type::build("bool"),
|
||||||
|
"true")));
|
||||||
|
REQUIRE(func.cv_qualifier() == cpp_cv_none);
|
||||||
|
REQUIRE(func.ref_qualifier() == cpp_ref_none);
|
||||||
|
}
|
||||||
|
else if (func.name() == "c")
|
||||||
|
{
|
||||||
|
REQUIRE(func.cv_qualifier() == cpp_cv_const);
|
||||||
|
REQUIRE(func.ref_qualifier() == cpp_ref_none);
|
||||||
|
}
|
||||||
|
else if (func.name() == "d")
|
||||||
|
{
|
||||||
|
REQUIRE(func.cv_qualifier() == cpp_cv_const_volatile);
|
||||||
|
REQUIRE(func.ref_qualifier() == cpp_ref_none);
|
||||||
|
}
|
||||||
|
else if (func.name() == "e")
|
||||||
|
{
|
||||||
|
REQUIRE(func.cv_qualifier() == cpp_cv_none);
|
||||||
|
REQUIRE(func.ref_qualifier() == cpp_ref_lvalue);
|
||||||
|
}
|
||||||
|
else if (func.name() == "f")
|
||||||
|
{
|
||||||
|
REQUIRE(func.cv_qualifier() == cpp_cv_const_volatile);
|
||||||
|
REQUIRE(func.ref_qualifier() == cpp_ref_rvalue);
|
||||||
|
}
|
||||||
|
else if (func.name() == "g")
|
||||||
|
{
|
||||||
|
REQUIRE(func.cv_qualifier() == cpp_cv_none);
|
||||||
|
REQUIRE(func.ref_qualifier() == cpp_ref_none);
|
||||||
|
REQUIRE(func.virtual_info());
|
||||||
|
if (func.parent().value().name() == "foo")
|
||||||
|
REQUIRE(func.virtual_info().value() == type_safe::flag_set<cpp_virtual_flags>{});
|
||||||
|
else if (func.parent().value().name() == "bar")
|
||||||
|
{
|
||||||
|
INFO(bool(func.virtual_info().value() & cpp_virtual_flags::override));
|
||||||
|
REQUIRE(func.virtual_info().value() == cpp_virtual_flags::override);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
REQUIRE(false);
|
||||||
|
}
|
||||||
|
else if (func.name() == "h")
|
||||||
|
{
|
||||||
|
REQUIRE(func.cv_qualifier() == cpp_cv_none);
|
||||||
|
REQUIRE(func.ref_qualifier() == cpp_ref_none);
|
||||||
|
REQUIRE(func.virtual_info());
|
||||||
|
if (func.parent().value().name() == "foo")
|
||||||
|
REQUIRE(func.virtual_info().value() == cpp_virtual_flags::pure);
|
||||||
|
else if (func.parent().value().name() == "bar")
|
||||||
|
REQUIRE(func.virtual_info().value()
|
||||||
|
== (cpp_virtual_flags::override | cpp_virtual_flags::final));
|
||||||
|
else
|
||||||
|
REQUIRE(false);
|
||||||
|
}
|
||||||
|
else if (func.name() == "i")
|
||||||
|
{
|
||||||
|
REQUIRE(func.cv_qualifier() == cpp_cv_none);
|
||||||
|
REQUIRE(func.ref_qualifier() == cpp_ref_none);
|
||||||
|
REQUIRE(func.body_kind() == cpp_function_definition);
|
||||||
|
}
|
||||||
|
else if (func.name() == "j")
|
||||||
|
{
|
||||||
|
REQUIRE(func.cv_qualifier() == cpp_cv_none);
|
||||||
|
REQUIRE(func.ref_qualifier() == cpp_ref_none);
|
||||||
|
REQUIRE(func.body_kind() == cpp_function_deleted);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
REQUIRE(false);
|
||||||
|
});
|
||||||
|
REQUIRE(count == 12u);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue