Add support for overloaded entity references

This commit is contained in:
Jonathan Müller 2017-03-19 16:07:54 +01:00
commit 08717b2ae7
8 changed files with 197 additions and 47 deletions

View file

@ -101,8 +101,9 @@ namespace cppast
private: private:
cpp_base_class(const cpp_type_ref& base, cpp_access_specifier_kind access, bool is_virtual) 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; cpp_entity_kind do_get_entity_kind() const noexcept override;

View file

@ -5,6 +5,10 @@
#ifndef CPPAST_CPP_ENTITY_REF_HPP_INCLUDED #ifndef CPPAST_CPP_ENTITY_REF_HPP_INCLUDED
#define CPPAST_CPP_ENTITY_REF_HPP_INCLUDED #define CPPAST_CPP_ENTITY_REF_HPP_INCLUDED
#include <vector>
#include <type_safe/variant.hpp>
#include <cppast/cpp_entity_index.hpp> #include <cppast/cpp_entity_index.hpp>
namespace cppast namespace cppast
@ -12,6 +16,10 @@ namespace cppast
enum class cpp_entity_kind; enum class cpp_entity_kind;
/// A basic reference to some kind of [cppast::cpp_entity](). /// 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 <typename T, typename Predicate> template <typename T, typename Predicate>
class basic_cpp_entity_ref 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<cpp_entity_id> 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. /// \returns The name of the reference, as spelled in the source code.
const std::string& name() const noexcept const std::string& name() const noexcept
{ {
return name_; return name_;
} }
/// \returns The [cppast::cpp_entity_id]() of the entity it refers to. /// \returns Whether or not it refers to multiple entities.
const cpp_entity_id& id() const noexcept bool is_overloaded() const noexcept
{ {
return target_; return target_.has_value(type_safe::variant_type<std::vector<cpp_entity_id>>{});
} }
/// \returns An optional reference to the [cppast::cpp_entity]() it refers to. /// \returns The number of entities it refers to.
type_safe::optional_ref<const T> get(const cpp_entity_index& idx) const noexcept type_safe::size_t no_overloaded() const noexcept
{ {
auto entity = idx.lookup(target_); return id().size();
if (!entity) }
return type_safe::nullopt;
DEBUG_ASSERT(Predicate{}(entity.value()), detail::precondition_error_handler{}, /// \returns An array reference to the id or ids it refers to.
"predicate not fulfilled"); type_safe::array_ref<const cpp_entity_id> id() const noexcept
return static_cast<const T&>(entity.value()); {
if (is_overloaded())
{
auto& vec = target_.value(type_safe::variant_type<std::vector<cpp_entity_id>>{});
return type_safe::ref(vec.data(), vec.size());
}
else
{
auto& id = target_.value(type_safe::variant_type<cpp_entity_id>{});
return type_safe::ref(&id, 1u);
}
} }
private: private:
cpp_entity_id target_; class ref_impl
std::string name_; {
public:
ref_impl(type_safe::object_ref<const cpp_entity_index> idx,
type_safe::array_ref<const cpp_entity_id> ids)
: ids_(ids), index_(idx)
{
}
class iterator
{
public:
using value_type = type_safe::optional_ref<const T>;
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<const ref_impl> ref, type_safe::index_t i)
: ref_(ref), i_(i)
{
}
type_safe::object_ref<const ref_impl> 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<const T> 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<const T&>(e);
});
}
type_safe::size_t size() const noexcept
{
return ids_.size();
}
private:
type_safe::array_ref<const cpp_entity_id> ids_;
type_safe::object_ref<const cpp_entity_index> 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<cpp_entity_id, std::vector<cpp_entity_id>> target_;
std::string name_;
}; };
/// \exclude /// \exclude

View file

@ -101,8 +101,9 @@ namespace cppast
cpp_entity_kind do_get_entity_kind() const noexcept override; cpp_entity_kind do_get_entity_kind() const noexcept override;
cpp_include_directive(const cpp_file_ref& target, cpp_include_kind kind) 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_; cpp_entity_id target_;

View file

@ -183,10 +183,10 @@ namespace cppast
/// \effects Sets the entity that is being templated and the primary template. /// \effects Sets the entity that is being templated and the primary template.
cpp_template_specialization(std::unique_ptr<cpp_entity> entity, cpp_template_specialization(std::unique_ptr<cpp_entity> entity,
const cpp_template_ref& templ) 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{}, DEBUG_ASSERT(!templ.is_overloaded() && templ.name() == entity->name(),
"invalid name of template ref"); detail::precondition_error_handler{}, "invalid name of template ref");
} }
private: private:

View file

@ -123,9 +123,9 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_using_directive(const detail::pars
namespace namespace
{ {
cpp_entity_id parse_entity_target_cursor(const CXCursor& cur) std::vector<cpp_entity_id> parse_entity_target_cursor(const CXCursor& cur)
{ {
cpp_entity_id result(""); std::vector<cpp_entity_id> result;
detail::visit_children(cur, detail::visit_children(cur,
[&](const CXCursor& child) { [&](const CXCursor& child) {
switch (clang_getCursorKind(child)) switch (clang_getCursorKind(child))
@ -136,7 +136,7 @@ namespace
case CXCursor_VariableRef: case CXCursor_VariableRef:
{ {
auto referenced = clang_getCursorReferenced(child); auto referenced = clang_getCursorReferenced(child);
result = detail::get_entity_id(referenced); result.push_back(detail::get_entity_id(referenced));
break; break;
} }
@ -144,12 +144,9 @@ namespace
{ {
auto size = clang_getNumOverloadedDecls(child); auto size = clang_getNumOverloadedDecls(child);
DEBUG_ASSERT(size >= 1u, detail::assert_handler{}); DEBUG_ASSERT(size >= 1u, detail::assert_handler{});
if (size == 1u) for (auto i = 0u; i != size; ++i)
result = detail::get_entity_id( result.push_back(detail::get_entity_id(
clang_getOverloadedDecl(child, 0u)); clang_getOverloadedDecl(child, i)));
else
result = detail::get_entity_id(
clang_getCursorReferenced(child));
break; break;
} }

View file

@ -179,7 +179,8 @@ struct g
REQUIRE(base.access_specifier() == cpp_private); REQUIRE(base.access_specifier() == cpp_private);
REQUIRE(!base.is_virtual()); 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);
REQUIRE(entity.value().name() == "a"); REQUIRE(entity.value().name() == "a");
} }
@ -188,7 +189,8 @@ struct g
REQUIRE(base.access_specifier() == cpp_private); REQUIRE(base.access_specifier() == cpp_private);
REQUIRE(!base.is_virtual()); 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);
REQUIRE(entity.value().name() == "d"); REQUIRE(entity.value().name() == "d");
} }
@ -214,7 +216,8 @@ struct g
REQUIRE(base.access_specifier() == cpp_public); REQUIRE(base.access_specifier() == cpp_public);
REQUIRE(!base.is_virtual()); 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);
REQUIRE(entity.value().name() == "base"); REQUIRE(entity.value().name() == "base");
REQUIRE(full_name(entity.value()) == "ns::base"); REQUIRE(full_name(entity.value()) == "ns::base");
@ -224,7 +227,8 @@ struct g
REQUIRE(base.access_specifier() == cpp_protected); REQUIRE(base.access_specifier() == cpp_protected);
REQUIRE(base.is_virtual()); 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);
REQUIRE(entity.value().name() == "e"); REQUIRE(entity.value().name() == "e");
} }
@ -250,7 +254,8 @@ struct g
REQUIRE(base.access_specifier() == cpp_public); REQUIRE(base.access_specifier() == cpp_public);
REQUIRE(!base.is_virtual()); 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);
REQUIRE(entity.value().name() == "base"); REQUIRE(entity.value().name() == "base");
REQUIRE(full_name(entity.value()) == "ns::base"); REQUIRE(full_name(entity.value()) == "ns::base");

View file

@ -77,8 +77,9 @@ namespace f = outer::c;
const char* target_full_name) { const char* target_full_name) {
auto& target = alias.target(); auto& target = alias.target();
REQUIRE(target.name() == target_name); REQUIRE(target.name() == target_name);
REQUIRE(!target.is_overloaded());
auto entity = target.get(idx); auto entity = target.get(idx)[0u];
REQUIRE(entity); REQUIRE(entity);
REQUIRE(full_name(entity.value()) == target_full_name); REQUIRE(full_name(entity.value()) == target_full_name);
}; };
@ -132,8 +133,9 @@ using namespace outer::ns;
cpp_entity_index idx; cpp_entity_index idx;
auto check_directive = [&](const cpp_using_directive& directive, const char* target_full_name) { auto check_directive = [&](const cpp_using_directive& directive, const char* target_full_name) {
auto target = directive.target(); auto target = directive.target();
REQUIRE(!target.is_overloaded());
auto entity = target.get(idx); auto entity = target.get(idx)[0u];
REQUIRE(entity); REQUIRE(entity);
REQUIRE(full_name(entity.value()) == target_full_name); REQUIRE(full_name(entity.value()) == target_full_name);
}; };
@ -164,7 +166,6 @@ using namespace outer::ns;
TEST_CASE("cpp_using_declaration") TEST_CASE("cpp_using_declaration")
{ {
// TODO: test overloaded functions
auto code = R"( auto code = R"(
namespace ns1 namespace ns1
{ {
@ -193,34 +194,49 @@ namespace outer
using outer::ns::c; using outer::ns::c;
using outer::c; using outer::c;
namespace ns
{
void d(int);
void d(float);
}
using ns::d;
)"; )";
cpp_entity_index idx; 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 target = decl.target();
REQUIRE((target.no_overloaded() == no));
auto entity = target.get(idx); for (auto entity : target.get(idx))
REQUIRE(entity); {
REQUIRE(full_name(entity.value()) == target_full_name); REQUIRE(entity);
REQUIRE(full_name(entity.value()) == target_full_name);
}
}; };
auto file = parse(idx, "cpp_using_declaration.cpp", code); auto file = parse(idx, "cpp_using_declaration.cpp", code);
auto count = test_visit<cpp_using_declaration>(*file, [&](const cpp_using_declaration& decl) { auto count = test_visit<cpp_using_declaration>(*file, [&](const cpp_using_declaration& decl) {
REQUIRE(decl.name().empty());
if (decl.target().name() == "ns1::a") if (decl.target().name() == "ns1::a")
check_declaration(decl, "ns1::a"); check_declaration(decl, "ns1::a", 1u);
else if (decl.target().name() == "ns2::b") 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") else if (decl.target().name() == "ns::c")
{ {
check_parent(decl, "outer", ""); 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") 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") 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 else
REQUIRE(false); REQUIRE(false);
}); });
REQUIRE(count == 5u); REQUIRE(count == 6u);
} }

View file

@ -28,7 +28,9 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
auto user_synthesized = static_cast<const cpp_user_defined_type&>(synthesized).entity(); auto user_synthesized = static_cast<const cpp_user_defined_type&>(synthesized).entity();
if (user_parsed.name() != user_synthesized.name()) if (user_parsed.name() != user_synthesized.name())
return false; 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() return entity.has_value() && (entity.value().name().empty()
|| full_name(entity.value()) == user_parsed.name()); || full_name(entity.value()) == user_parsed.name());
} }