From 08717b2ae7e8487b05370a9aaa06e23f2d3f9999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Sun, 19 Mar 2017 16:07:54 +0100 Subject: [PATCH] Add support for overloaded entity references --- include/cppast/cpp_class.hpp | 3 +- include/cppast/cpp_entity_ref.hpp | 154 +++++++++++++++++++++++++--- include/cppast/cpp_preprocessor.hpp | 3 +- include/cppast/cpp_template.hpp | 6 +- src/libclang/namespace_parser.cpp | 15 ++- test/cpp_class.cpp | 15 ++- test/cpp_namespace.cpp | 44 +++++--- test/cpp_type_alias.cpp | 4 +- 8 files changed, 197 insertions(+), 47 deletions(-) diff --git a/include/cppast/cpp_class.hpp b/include/cppast/cpp_class.hpp index cf65fcf..4a5cfca 100644 --- a/include/cppast/cpp_class.hpp +++ b/include/cppast/cpp_class.hpp @@ -101,8 +101,9 @@ namespace cppast private: cpp_base_class(const cpp_type_ref& base, cpp_access_specifier_kind access, bool is_virtual) - : cpp_entity(base.name()), base_id_(base.id()), access_(access), virtual_(is_virtual) + : cpp_entity(base.name()), base_id_(base.id()[0u]), access_(access), virtual_(is_virtual) { + DEBUG_ASSERT(!base.is_overloaded(), detail::precondition_error_handler{}); } cpp_entity_kind do_get_entity_kind() const noexcept override; diff --git a/include/cppast/cpp_entity_ref.hpp b/include/cppast/cpp_entity_ref.hpp index 0a63d07..c1c89e6 100644 --- a/include/cppast/cpp_entity_ref.hpp +++ b/include/cppast/cpp_entity_ref.hpp @@ -5,6 +5,10 @@ #ifndef CPPAST_CPP_ENTITY_REF_HPP_INCLUDED #define CPPAST_CPP_ENTITY_REF_HPP_INCLUDED +#include + +#include + #include namespace cppast @@ -12,6 +16,10 @@ namespace cppast enum class cpp_entity_kind; /// A basic reference to some kind of [cppast::cpp_entity](). + /// + /// It can either refer to a single [cppast::cpp_entity]() + /// or multiple. + /// In the later case it is *overloaded*. template class basic_cpp_entity_ref { @@ -22,32 +30,152 @@ namespace cppast { } + /// \effects Creates it giving it multiple target ids and name. + /// \notes This is to refer to an overloaded function. + basic_cpp_entity_ref(std::vector target_ids, std::string target_name) + : target_(std::move(target_ids)), name_(std::move(target_name)) + { + } + /// \returns The name of the reference, as spelled in the source code. const std::string& name() const noexcept { return name_; } - /// \returns The [cppast::cpp_entity_id]() of the entity it refers to. - const cpp_entity_id& id() const noexcept + /// \returns Whether or not it refers to multiple entities. + bool is_overloaded() const noexcept { - return target_; + return target_.has_value(type_safe::variant_type>{}); } - /// \returns An optional reference to the [cppast::cpp_entity]() it refers to. - type_safe::optional_ref get(const cpp_entity_index& idx) const noexcept + /// \returns The number of entities it refers to. + type_safe::size_t no_overloaded() const noexcept { - auto entity = idx.lookup(target_); - if (!entity) - return type_safe::nullopt; - DEBUG_ASSERT(Predicate{}(entity.value()), detail::precondition_error_handler{}, - "predicate not fulfilled"); - return static_cast(entity.value()); + return id().size(); + } + + /// \returns An array reference to the id or ids it refers to. + type_safe::array_ref id() const noexcept + { + if (is_overloaded()) + { + auto& vec = target_.value(type_safe::variant_type>{}); + return type_safe::ref(vec.data(), vec.size()); + } + else + { + auto& id = target_.value(type_safe::variant_type{}); + return type_safe::ref(&id, 1u); + } } private: - cpp_entity_id target_; - std::string name_; + class ref_impl + { + public: + ref_impl(type_safe::object_ref idx, + type_safe::array_ref ids) + : ids_(ids), index_(idx) + { + } + + class iterator + { + public: + using value_type = type_safe::optional_ref; + using reference = value_type; + using pointer = value_type*; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + reference operator*() const noexcept + { + return (*ref_)[i_]; + } + + pointer operator->() const noexcept + { + return &(*ref_)[i_]; + } + + iterator& operator++() noexcept + { + ++i_; + return *this; + } + + iterator operator++(int)noexcept + { + auto tmp = *this; + ++(*this); + return tmp; + } + + friend bool operator==(const iterator& a, const iterator& b) noexcept + { + return a.ref_ == b.ref_ && a.i_ == b.i_; + } + + friend bool operator!=(const iterator& a, const iterator& b) noexcept + { + return !(a == b); + } + + private: + iterator(type_safe::object_ref ref, type_safe::index_t i) + : ref_(ref), i_(i) + { + } + + type_safe::object_ref ref_; + type_safe::index_t i_; + + friend ref_impl; + }; + + iterator begin() const noexcept + { + return iterator(type_safe::ref(*this), 0u); + } + + iterator end() const noexcept + { + return iterator(type_safe::ref(*this), size()); + } + + type_safe::optional_ref operator[](type_safe::index_t i) const + { + return index_->lookup(ids_[i]).map([](const cpp_entity& e) -> const T& { + DEBUG_ASSERT(Predicate{}(e), detail::precondition_error_handler{}, + "predicate not fulfilled"); + return static_cast(e); + }); + } + + type_safe::size_t size() const noexcept + { + return ids_.size(); + } + + private: + type_safe::array_ref ids_; + type_safe::object_ref index_; + }; + + public: + /// \returns An array reference to the entities it refers to. + /// The return type provides `operator[]` + `size()`, + /// as well as `begin()` and `end()` returning forward iterators. + /// \exclude return + ref_impl get(const cpp_entity_index& idx) const noexcept + { + return ref_impl(type_safe::ref(idx), id()); + } + + private: + type_safe::variant> target_; + std::string name_; }; /// \exclude diff --git a/include/cppast/cpp_preprocessor.hpp b/include/cppast/cpp_preprocessor.hpp index fc3fbb2..53a9b77 100644 --- a/include/cppast/cpp_preprocessor.hpp +++ b/include/cppast/cpp_preprocessor.hpp @@ -101,8 +101,9 @@ namespace cppast cpp_entity_kind do_get_entity_kind() const noexcept override; cpp_include_directive(const cpp_file_ref& target, cpp_include_kind kind) - : cpp_entity(target.name()), target_(target.id()), kind_(kind) + : cpp_entity(target.name()), target_(target.id()[0u]), kind_(kind) { + DEBUG_ASSERT(!target.is_overloaded(), detail::precondition_error_handler{}); } cpp_entity_id target_; diff --git a/include/cppast/cpp_template.hpp b/include/cppast/cpp_template.hpp index 5c0cc2f..35beb42 100644 --- a/include/cppast/cpp_template.hpp +++ b/include/cppast/cpp_template.hpp @@ -183,10 +183,10 @@ namespace cppast /// \effects Sets the entity that is being templated and the primary template. cpp_template_specialization(std::unique_ptr entity, const cpp_template_ref& templ) - : cpp_template(std::move(entity)), templ_(templ.id()) + : cpp_template(std::move(entity)), templ_(templ.id()[0u]) { - DEBUG_ASSERT(templ.name() == entity->name(), detail::precondition_error_handler{}, - "invalid name of template ref"); + DEBUG_ASSERT(!templ.is_overloaded() && templ.name() == entity->name(), + detail::precondition_error_handler{}, "invalid name of template ref"); } private: diff --git a/src/libclang/namespace_parser.cpp b/src/libclang/namespace_parser.cpp index 85c9139..6f8c380 100644 --- a/src/libclang/namespace_parser.cpp +++ b/src/libclang/namespace_parser.cpp @@ -123,9 +123,9 @@ std::unique_ptr detail::parse_cpp_using_directive(const detail::pars namespace { - cpp_entity_id parse_entity_target_cursor(const CXCursor& cur) + std::vector parse_entity_target_cursor(const CXCursor& cur) { - cpp_entity_id result(""); + std::vector result; detail::visit_children(cur, [&](const CXCursor& child) { switch (clang_getCursorKind(child)) @@ -136,7 +136,7 @@ namespace case CXCursor_VariableRef: { auto referenced = clang_getCursorReferenced(child); - result = detail::get_entity_id(referenced); + result.push_back(detail::get_entity_id(referenced)); break; } @@ -144,12 +144,9 @@ namespace { auto size = clang_getNumOverloadedDecls(child); DEBUG_ASSERT(size >= 1u, detail::assert_handler{}); - if (size == 1u) - result = detail::get_entity_id( - clang_getOverloadedDecl(child, 0u)); - else - result = detail::get_entity_id( - clang_getCursorReferenced(child)); + for (auto i = 0u; i != size; ++i) + result.push_back(detail::get_entity_id( + clang_getOverloadedDecl(child, i))); break; } diff --git a/test/cpp_class.cpp b/test/cpp_class.cpp index 7e1f1e3..31aef08 100644 --- a/test/cpp_class.cpp +++ b/test/cpp_class.cpp @@ -179,7 +179,8 @@ struct g REQUIRE(base.access_specifier() == cpp_private); REQUIRE(!base.is_virtual()); - auto entity = base.entity().get(idx); + REQUIRE(!base.entity().is_overloaded()); + auto entity = base.entity().get(idx)[0u]; REQUIRE(entity); REQUIRE(entity.value().name() == "a"); } @@ -188,7 +189,8 @@ struct g REQUIRE(base.access_specifier() == cpp_private); REQUIRE(!base.is_virtual()); - auto entity = base.entity().get(idx); + REQUIRE(!base.entity().is_overloaded()); + auto entity = base.entity().get(idx)[0u]; REQUIRE(entity); REQUIRE(entity.value().name() == "d"); } @@ -214,7 +216,8 @@ struct g REQUIRE(base.access_specifier() == cpp_public); REQUIRE(!base.is_virtual()); - auto entity = base.entity().get(idx); + REQUIRE(!base.entity().is_overloaded()); + auto entity = base.entity().get(idx)[0u]; REQUIRE(entity); REQUIRE(entity.value().name() == "base"); REQUIRE(full_name(entity.value()) == "ns::base"); @@ -224,7 +227,8 @@ struct g REQUIRE(base.access_specifier() == cpp_protected); REQUIRE(base.is_virtual()); - auto entity = base.entity().get(idx); + REQUIRE(!base.entity().is_overloaded()); + auto entity = base.entity().get(idx)[0u]; REQUIRE(entity); REQUIRE(entity.value().name() == "e"); } @@ -250,7 +254,8 @@ struct g REQUIRE(base.access_specifier() == cpp_public); REQUIRE(!base.is_virtual()); - auto entity = base.entity().get(idx); + REQUIRE(!base.entity().is_overloaded()); + auto entity = base.entity().get(idx)[0u]; REQUIRE(entity); REQUIRE(entity.value().name() == "base"); REQUIRE(full_name(entity.value()) == "ns::base"); diff --git a/test/cpp_namespace.cpp b/test/cpp_namespace.cpp index 66032c0..f9df545 100644 --- a/test/cpp_namespace.cpp +++ b/test/cpp_namespace.cpp @@ -77,8 +77,9 @@ namespace f = outer::c; const char* target_full_name) { auto& target = alias.target(); REQUIRE(target.name() == target_name); + REQUIRE(!target.is_overloaded()); - auto entity = target.get(idx); + auto entity = target.get(idx)[0u]; REQUIRE(entity); REQUIRE(full_name(entity.value()) == target_full_name); }; @@ -132,8 +133,9 @@ using namespace outer::ns; cpp_entity_index idx; auto check_directive = [&](const cpp_using_directive& directive, const char* target_full_name) { auto target = directive.target(); + REQUIRE(!target.is_overloaded()); - auto entity = target.get(idx); + auto entity = target.get(idx)[0u]; REQUIRE(entity); REQUIRE(full_name(entity.value()) == target_full_name); }; @@ -164,7 +166,6 @@ using namespace outer::ns; TEST_CASE("cpp_using_declaration") { - // TODO: test overloaded functions auto code = R"( namespace ns1 { @@ -193,34 +194,49 @@ namespace outer using outer::ns::c; using outer::c; + +namespace ns +{ + void d(int); + void d(float); +} + +using ns::d; )"; cpp_entity_index idx; - auto check_declaration = [&](const cpp_using_declaration& decl, const char* target_full_name) { + auto check_declaration = [&](const cpp_using_declaration& decl, const char* target_full_name, + unsigned no) { auto target = decl.target(); - - auto entity = target.get(idx); - REQUIRE(entity); - REQUIRE(full_name(entity.value()) == target_full_name); + REQUIRE((target.no_overloaded() == no)); + for (auto entity : target.get(idx)) + { + REQUIRE(entity); + REQUIRE(full_name(entity.value()) == target_full_name); + } }; auto file = parse(idx, "cpp_using_declaration.cpp", code); auto count = test_visit(*file, [&](const cpp_using_declaration& decl) { + REQUIRE(decl.name().empty()); + if (decl.target().name() == "ns1::a") - check_declaration(decl, "ns1::a"); + check_declaration(decl, "ns1::a", 1u); else if (decl.target().name() == "ns2::b") - check_declaration(decl, "ns2::b"); + check_declaration(decl, "ns2::b", 1u); else if (decl.target().name() == "ns::c") { check_parent(decl, "outer", ""); - check_declaration(decl, "outer::ns::c"); + check_declaration(decl, "outer::ns::c", 1u); } else if (decl.target().name() == "outer::ns::c") - check_declaration(decl, "outer::ns::c"); + check_declaration(decl, "outer::ns::c", 1u); else if (decl.target().name() == "outer::c") - check_declaration(decl, "outer::ns::c"); + check_declaration(decl, "outer::ns::c", 1u); + else if (decl.target().name() == "ns::d") + check_declaration(decl, "ns::d", 2u); else REQUIRE(false); }); - REQUIRE(count == 5u); + REQUIRE(count == 6u); } diff --git a/test/cpp_type_alias.cpp b/test/cpp_type_alias.cpp index d11e3f4..ad51c9e 100644 --- a/test/cpp_type_alias.cpp +++ b/test/cpp_type_alias.cpp @@ -28,7 +28,9 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_ auto user_synthesized = static_cast(synthesized).entity(); if (user_parsed.name() != user_synthesized.name()) return false; - auto entity = user_parsed.get(idx); + else if (user_parsed.is_overloaded()) + return false; + auto entity = user_parsed.get(idx)[0u]; return entity.has_value() && (entity.value().name().empty() || full_name(entity.value()) == user_parsed.name()); }