diff --git a/include/cppast/cpp_member_function.hpp b/include/cppast/cpp_member_function.hpp index b4f4fa2..515d54a 100644 --- a/include/cppast/cpp_member_function.hpp +++ b/include/cppast/cpp_member_function.hpp @@ -294,6 +294,8 @@ namespace cppast class cpp_destructor final : public cpp_function_base { public: + static cpp_entity_kind kind() noexcept; + /// Builds a [cppast::cpp_destructor](). class builder : public basic_builder { @@ -331,6 +333,8 @@ namespace cppast cpp_entity_kind do_get_entity_kind() const noexcept override; cpp_virtual virtual_; + + friend basic_builder; }; } // namespace cppast diff --git a/src/cpp_member_function.cpp b/src/cpp_member_function.cpp index 5233c8d..1cf8fd4 100644 --- a/src/cpp_member_function.cpp +++ b/src/cpp_member_function.cpp @@ -38,7 +38,12 @@ cpp_entity_kind cpp_constructor::do_get_entity_kind() const noexcept return kind(); } -cpp_entity_kind cpp_destructor::do_get_entity_kind() const noexcept +cpp_entity_kind cpp_destructor::kind() noexcept { return cpp_entity_kind::destructor_t; } + +cpp_entity_kind cpp_destructor::do_get_entity_kind() const noexcept +{ + return kind(); +} diff --git a/src/libclang/function_parser.cpp b/src/libclang/function_parser.cpp index 9ac10b0..50b3280 100644 --- a/src/libclang/function_parser.cpp +++ b/src/libclang/function_parser.cpp @@ -373,13 +373,29 @@ namespace } } + template + auto set_qualifier(int, Builder& b, cpp_cv cv, cpp_reference ref) + -> decltype(b.cv_ref_qualifier(cv, ref), true) + { + b.cv_ref_qualifier(cv, ref); + return true; + } + + template + bool set_qualifier(short, Builder&, cpp_cv, cpp_reference) + { + return false; + } + template std::unique_ptr 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, true, true); - builder.cv_ref_qualifier(suffix.cv_qualifier, suffix.ref_qualifier); + auto allow_qualifiers = set_qualifier(0, builder, cpp_cv_none, cpp_ref_none); + + auto suffix = parse_suffix_info(stream, context, allow_qualifiers, true); + set_qualifier(0, builder, 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)) @@ -434,7 +450,7 @@ std::unique_ptr detail::parse_cpp_conversion_op(const detail::parse_ else if (detail::skip_if(stream, "explicit")) builder.is_explicit(); else - stream.bump(); + DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, "unexpected token"); } // heuristic to find arguments tokens @@ -483,7 +499,7 @@ std::unique_ptr detail::parse_cpp_constructor(const detail::parse_co else if (detail::skip_if(stream, "explicit")) builder.is_explicit(); else - stream.bump(); + DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, "unexpected token"); } skip_parameters(stream); @@ -494,3 +510,21 @@ std::unique_ptr detail::parse_cpp_constructor(const detail::parse_co return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind); } + +std::unique_ptr detail::parse_cpp_destructor(const detail::parse_context& context, + const CXCursor& cur) +{ + DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_Destructor, detail::assert_handler{}); + + detail::tokenizer tokenizer(context.tu, context.file, cur); + detail::token_stream stream(tokenizer, cur); + + auto is_virtual = detail::skip_if(stream, "virtual"); + detail::skip(stream, "~"); + auto name = std::string("~") + stream.get().c_str(); + cpp_destructor::builder builder(std::move(name)); + + detail::skip(stream, "("); + detail::skip(stream, ")"); + return handle_suffix(context, cur, builder, stream, is_virtual); +} diff --git a/src/libclang/parse_functions.cpp b/src/libclang/parse_functions.cpp index 6f3d261..8105448 100644 --- a/src/libclang/parse_functions.cpp +++ b/src/libclang/parse_functions.cpp @@ -96,6 +96,8 @@ std::unique_ptr detail::parse_entity(const detail::parse_context& co return parse_cpp_conversion_op(context, cur); case CXCursor_Constructor: return parse_cpp_constructor(context, cur); + case CXCursor_Destructor: + return parse_cpp_destructor(context, cur); default: break; diff --git a/src/libclang/parse_functions.hpp b/src/libclang/parse_functions.hpp index 74132c8..f06ccf9 100644 --- a/src/libclang/parse_functions.hpp +++ b/src/libclang/parse_functions.hpp @@ -93,6 +93,8 @@ namespace cppast const CXCursor& cur); std::unique_ptr parse_cpp_constructor(const parse_context& context, const CXCursor& cur); + std::unique_ptr parse_cpp_destructor(const parse_context& context, + const CXCursor& cur); std::unique_ptr parse_entity(const parse_context& context, const CXCursor& cur); } diff --git a/test/cpp_member_function.cpp b/test/cpp_member_function.cpp index 4b7041c..86352b6 100644 --- a/test/cpp_member_function.cpp +++ b/test/cpp_member_function.cpp @@ -202,6 +202,7 @@ struct foo auto file = parse(idx, "cpp_constructor.cpp", code); auto count = test_visit(*file, [&](const cpp_constructor& cont) { REQUIRE(!cont.is_variadic()); + REQUIRE(cont.name() == "foo"); if (count_children(cont) == 0u) { @@ -233,3 +234,68 @@ struct foo }); REQUIRE(count == 3u); } + +TEST_CASE("cpp_destructor") +{ + auto code = R"( +struct a +{ + ~a(); +}; + +struct b +{ + ~b() noexcept(false) {} +}; + +struct c +{ + virtual ~c() = default; +}; + +struct d : c +{ + ~d() final; +}; +)"; + + auto file = parse({}, "cpp_destructor.cpp", code); + auto count = test_visit(*file, [&](const cpp_destructor& dtor) { + REQUIRE(count_children(dtor) == 0u); + REQUIRE(!dtor.is_variadic()); + + if (dtor.name() == "~a") + { + REQUIRE(!dtor.is_virtual()); + REQUIRE(dtor.is_declaration()); + REQUIRE(!dtor.noexcept_condition()); + } + else if (dtor.name() == "~b") + { + REQUIRE(!dtor.is_virtual()); + REQUIRE(dtor.body_kind() == cpp_function_definition); + REQUIRE(dtor.noexcept_condition()); + REQUIRE( + equal_expressions(dtor.noexcept_condition().value(), + *cpp_unexposed_expression::build(cpp_builtin_type::build("bool"), + "false"))); + } + else if (dtor.name() == "~c") + { + REQUIRE(dtor.virtual_info()); + REQUIRE(dtor.virtual_info().value() == type_safe::flag_set{}); + REQUIRE(dtor.body_kind() == cpp_function_defaulted); + REQUIRE(!dtor.noexcept_condition()); + } + else if (dtor.name() == "~d") + { + REQUIRE(dtor.virtual_info()); + REQUIRE(dtor.virtual_info().value() + == (cpp_virtual_flags::override | cpp_virtual_flags::final)); + REQUIRE(!dtor.noexcept_condition()); + } + else + REQUIRE(false); + }); + REQUIRE(count == 4u); +}