Parse cpp_converison_op
This commit is contained in:
parent
85ca226117
commit
8691628ec5
9 changed files with 184 additions and 25 deletions
|
|
@ -146,6 +146,9 @@ namespace cppast
|
|||
{
|
||||
static_cast<cpp_member_function_base&>(*this->function).constexpr_ = true;
|
||||
}
|
||||
|
||||
protected:
|
||||
basic_member_builder() noexcept = default;
|
||||
};
|
||||
|
||||
/// \effects Sets name and return type, as well as the rest to defaults.
|
||||
|
|
@ -173,10 +176,11 @@ namespace cppast
|
|||
static cpp_entity_kind kind() noexcept;
|
||||
|
||||
/// Builder for [cppast::cpp_member_function]().
|
||||
class builder : public basic_member_builder<cpp_member_function>
|
||||
class builder : public cpp_member_function_base::basic_member_builder<cpp_member_function>
|
||||
{
|
||||
public:
|
||||
using basic_member_builder::basic_member_builder;
|
||||
using cpp_member_function_base::basic_member_builder<cpp_member_function>::
|
||||
basic_member_builder;
|
||||
};
|
||||
|
||||
private:
|
||||
|
|
@ -191,17 +195,29 @@ namespace cppast
|
|||
class cpp_conversion_op final : public cpp_member_function_base
|
||||
{
|
||||
public:
|
||||
static cpp_entity_kind kind() noexcept;
|
||||
|
||||
/// Builder for [cppast::cpp_conversion_op]().
|
||||
class builder : public basic_member_builder<cpp_conversion_op>
|
||||
{
|
||||
public:
|
||||
using basic_member_builder::basic_member_builder;
|
||||
/// \effects Creates it giving it the return type.
|
||||
/// \notes It does not have a name as it is given by the return type.
|
||||
builder(std::unique_ptr<cpp_type> type)
|
||||
{
|
||||
function =
|
||||
std::unique_ptr<cpp_conversion_op>(new cpp_conversion_op(std::move(type)));
|
||||
}
|
||||
|
||||
/// \effects Marks the conversion operator `explicit`.
|
||||
void is_explicit() noexcept
|
||||
{
|
||||
function->explicit_ = true;
|
||||
}
|
||||
|
||||
private:
|
||||
using basic_member_builder::is_variadic;
|
||||
using basic_member_builder::add_parameter;
|
||||
};
|
||||
|
||||
/// \returns Whether or not the conversion is `explicit`.
|
||||
|
|
@ -211,14 +227,16 @@ namespace cppast
|
|||
}
|
||||
|
||||
private:
|
||||
cpp_conversion_op(std::string name, std::unique_ptr<cpp_type> return_t)
|
||||
: cpp_member_function_base(std::move(name), std::move(return_t)), explicit_(false)
|
||||
cpp_conversion_op(std::unique_ptr<cpp_type> return_t)
|
||||
: cpp_member_function_base("", std::move(return_t)), explicit_(false)
|
||||
{
|
||||
}
|
||||
|
||||
cpp_entity_kind do_get_entity_kind() const noexcept override;
|
||||
|
||||
bool explicit_;
|
||||
|
||||
friend basic_member_builder<cpp_conversion_op>;
|
||||
};
|
||||
|
||||
/// A [cppast::cpp_entity]() modelling a C++ constructor.
|
||||
|
|
|
|||
|
|
@ -18,11 +18,16 @@ 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::kind() noexcept
|
||||
{
|
||||
return cpp_entity_kind::conversion_op_t;
|
||||
}
|
||||
|
||||
cpp_entity_kind cpp_conversion_op::do_get_entity_kind() const noexcept
|
||||
{
|
||||
return kind();
|
||||
}
|
||||
|
||||
cpp_entity_kind cpp_constructor::do_get_entity_kind() const noexcept
|
||||
{
|
||||
return cpp_entity_kind::constructor_t;
|
||||
|
|
|
|||
|
|
@ -66,7 +66,8 @@ namespace
|
|||
prefix_info result;
|
||||
|
||||
// just check for keywords until we've reached the function name
|
||||
while (!detail::skip_if(stream, name.c_str()))
|
||||
// notes: name can have multiple tokens if it is an operator
|
||||
while (!detail::skip_if(stream, name.c_str(), true))
|
||||
{
|
||||
if (detail::skip_if(stream, "constexpr"))
|
||||
result.is_constexpr = true;
|
||||
|
|
@ -270,7 +271,7 @@ namespace
|
|||
|
||||
auto prefix = parse_prefix_info(stream, name);
|
||||
DEBUG_ASSERT(!prefix.is_virtual, detail::parse_error_handler{}, cur,
|
||||
"unexpected tokens in function prefix");
|
||||
"free function cannot be virtual");
|
||||
if (prefix.is_constexpr)
|
||||
builder.is_constexpr();
|
||||
|
||||
|
|
@ -356,6 +357,21 @@ namespace
|
|||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Builder>
|
||||
std::unique_ptr<cpp_entity> handle_suffix(const detail::parse_context& context,
|
||||
const CXCursor& cur, Builder& builder,
|
||||
detail::token_stream& stream, bool is_virtual)
|
||||
{
|
||||
auto suffix = parse_suffix_info(stream, context);
|
||||
builder.cv_ref_qualifier(suffix.cv_qualifier, suffix.ref_qualifier);
|
||||
if (suffix.noexcept_condition)
|
||||
builder.noexcept_condition(move(suffix.noexcept_condition));
|
||||
if (auto virt = calculate_virtual(cur, is_virtual, suffix.virtual_keywords))
|
||||
builder.virtual_info(virt.value());
|
||||
|
||||
return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_entity> detail::parse_cpp_member_function(const detail::parse_context& context,
|
||||
|
|
@ -379,13 +395,53 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_member_function(const detail::pars
|
|||
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);
|
||||
return handle_suffix(context, cur, builder, stream, prefix.is_virtual);
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_entity> detail::parse_cpp_conversion_op(const detail::parse_context& context,
|
||||
const CXCursor& cur)
|
||||
{
|
||||
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_ConversionFunction, detail::assert_handler{});
|
||||
cpp_conversion_op::builder builder(detail::parse_type(context, clang_getCursorResultType(cur)));
|
||||
|
||||
detail::tokenizer tokenizer(context.tu, context.file, cur);
|
||||
detail::token_stream stream(tokenizer, cur);
|
||||
|
||||
// look for constexpr, explicit, virtual
|
||||
// must come before the operator token
|
||||
auto is_virtual = false;
|
||||
while (!detail::skip_if(stream, "operator"))
|
||||
{
|
||||
if (detail::skip_if(stream, "virtual"))
|
||||
is_virtual = true;
|
||||
else if (detail::skip_if(stream, "constexpr"))
|
||||
builder.is_constexpr();
|
||||
else if (detail::skip_if(stream, "explicit"))
|
||||
builder.is_explicit();
|
||||
else
|
||||
stream.bump();
|
||||
}
|
||||
|
||||
// heuristic to find arguments tokens
|
||||
// skip forward, skipping inside brackets
|
||||
while (true)
|
||||
{
|
||||
if (detail::skip_if(stream, "("))
|
||||
{
|
||||
if (detail::skip_if(stream, ")"))
|
||||
break;
|
||||
else
|
||||
detail::skip_brackets(stream);
|
||||
}
|
||||
else if (detail::skip_if(stream, "["))
|
||||
detail::skip_brackets(stream);
|
||||
else if (detail::skip_if(stream, "{"))
|
||||
detail::skip_brackets(stream);
|
||||
else if (detail::skip_if(stream, "<"))
|
||||
detail::skip_brackets(stream);
|
||||
else
|
||||
stream.bump();
|
||||
}
|
||||
|
||||
return handle_suffix(context, cur, builder, stream, is_virtual);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,6 +92,8 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
|
|||
if (auto func = try_parse_static_cpp_function(context, cur))
|
||||
return func;
|
||||
return parse_cpp_member_function(context, cur);
|
||||
case CXCursor_ConversionFunction:
|
||||
return parse_cpp_conversion_op(context, cur);
|
||||
|
||||
default:
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -89,6 +89,8 @@ namespace cppast
|
|||
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_cpp_conversion_op(const parse_context& context,
|
||||
const CXCursor& cur);
|
||||
|
||||
std::unique_ptr<cpp_entity> parse_entity(const parse_context& context, const CXCursor& cur);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,12 +170,32 @@ void detail::skip(detail::token_stream& stream, const char* str)
|
|||
stream.bump();
|
||||
}
|
||||
|
||||
bool detail::skip_if(detail::token_stream& stream, const char* str)
|
||||
namespace
|
||||
{
|
||||
auto& token = stream.peek();
|
||||
if (token != str)
|
||||
return false;
|
||||
stream.bump();
|
||||
bool starts_with(const char*& str, const detail::token& t)
|
||||
{
|
||||
if (std::strncmp(str, t.c_str(), t.value().length()) != 0)
|
||||
return false;
|
||||
str += t.value().length();
|
||||
while (*str == ' ' || *str == '\t')
|
||||
++str;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool detail::skip_if(detail::token_stream& stream, const char* str, bool multi_token)
|
||||
{
|
||||
auto save = stream.cur();
|
||||
do
|
||||
{
|
||||
auto& token = stream.peek();
|
||||
if (!starts_with(str, token) || (!multi_token && *str != '\0'))
|
||||
{
|
||||
stream.set_cur(save);
|
||||
return false;
|
||||
}
|
||||
stream.bump();
|
||||
} while (multi_token && *str);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -154,7 +154,8 @@ namespace cppast
|
|||
void skip(token_stream& stream, const char* str);
|
||||
|
||||
// skips the next token if it has the given string
|
||||
bool skip_if(token_stream& stream, const char* str);
|
||||
// if multi_token == true, str can consist of multiple tokens optionally separated by whitespace
|
||||
bool skip_if(token_stream& stream, const char* str, bool multi_token = false);
|
||||
|
||||
// returns the location of the closing bracket
|
||||
// the current token must be (,[,{ or <
|
||||
|
|
|
|||
|
|
@ -130,3 +130,58 @@ struct bar : foo
|
|||
});
|
||||
REQUIRE(count == 12u);
|
||||
}
|
||||
|
||||
TEST_CASE("cpp_conversion_op")
|
||||
{
|
||||
auto code = R"(
|
||||
namespace ns
|
||||
{
|
||||
using type = char;
|
||||
}
|
||||
|
||||
// most of it only need to be check in member function
|
||||
struct foo
|
||||
{
|
||||
operator int&();
|
||||
explicit operator bool() const;
|
||||
constexpr operator ns::type();
|
||||
};
|
||||
)";
|
||||
|
||||
cpp_entity_index idx;
|
||||
auto file = parse(idx, "cpp_conversion_op.cpp", code);
|
||||
auto count = test_visit<cpp_conversion_op>(*file, [&](const cpp_conversion_op& op) {
|
||||
REQUIRE(op.name().empty());
|
||||
REQUIRE(count_children(op) == 0u);
|
||||
REQUIRE(!op.is_variadic());
|
||||
REQUIRE(op.body_kind() == cpp_function_declaration);
|
||||
REQUIRE(op.ref_qualifier() == cpp_ref_none);
|
||||
REQUIRE(!op.virtual_info());
|
||||
REQUIRE(!op.noexcept_condition());
|
||||
|
||||
if (!op.is_explicit() && !op.is_constexpr())
|
||||
{
|
||||
REQUIRE(equal_types(idx, op.return_type(),
|
||||
*cpp_reference_type::build(cpp_builtin_type::build("int"),
|
||||
cpp_ref_lvalue)));
|
||||
REQUIRE(op.cv_qualifier() == cpp_cv_none);
|
||||
REQUIRE(!op.is_explicit());
|
||||
REQUIRE(!op.is_constexpr());
|
||||
}
|
||||
else if (op.is_explicit() && !op.is_constexpr())
|
||||
{
|
||||
REQUIRE(equal_types(idx, op.return_type(), *cpp_builtin_type::build("bool")));
|
||||
REQUIRE(op.cv_qualifier() == cpp_cv_const);
|
||||
}
|
||||
else if (!op.is_explicit() && op.is_constexpr())
|
||||
{
|
||||
REQUIRE(equal_types(idx, op.return_type(),
|
||||
*cpp_user_defined_type::build(
|
||||
cpp_type_ref(cpp_entity_id(""), "ns::type"))));
|
||||
REQUIRE(op.cv_qualifier() == cpp_cv_none);
|
||||
}
|
||||
else
|
||||
REQUIRE(false);
|
||||
});
|
||||
REQUIRE(count == 3u);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
|
|||
if (user_parsed.name() != user_synthesized.name())
|
||||
return false;
|
||||
auto entity = user_parsed.get(idx);
|
||||
return entity.has_value() && entity.value().name().empty()
|
||||
|| entity.value().name() == user_parsed.name();
|
||||
return entity.has_value() && (entity.value().name().empty()
|
||||
|| full_name(entity.value()) == user_parsed.name());
|
||||
}
|
||||
|
||||
case cpp_type_kind::cv_qualified:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue